Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(api-form-builder): form statistics #3780

Merged
merged 15 commits into from
Feb 14, 2024

Conversation

neatbyte-vnobis
Copy link
Collaborator

Changes

Move form statistics to a separate fbFormStat content model.

How Has This Been Tested?

Manually.

Documentation

Form Builder - Move backend to HCMS

@neatbyte-vnobis
Copy link
Collaborator Author

@adrians5j
Tests with ddb storage are passing, while the ddb-es storage tests are failing. I've looked into this issue but can't quite figure out the cause. Do you have any ideas why this might happen?

The failure occurs in deleteForm function, particularly with the newly added deleteFormStats. This function utilizes the deleteMultipleEntries method from the api-headless-cms package, which seems to be unused elsewhere in the code. I'm not entirely sure, but this could potentially be the root cause of the problem.

I want to investigate this issue further, but I'm not sure how to run tests with ddb-es storage locally. Is there a way to do this?

@adrians5j
Copy link
Member

Yeah @neatbyte-vnobis , it's a little bit tricky but it can be done.

I'll send some notes.

@neatbyte-vnobis
Copy link
Collaborator Author

@adrians5j
The issue was inside deleteMultipleEntries method. This method was attempting to delete items that did not exist in the ddb-es table. Specifically, in this test, it attempted to delete both published and latest formStats, but no published formStats were present.

To address this, I've implemented a solution based on the existing deleteEntry method. This method first queries for all entry items before deleting them, rather than directly deleting the latest and published items for all provided ids.

The tests for the ddb table were functioning correctly because DynamoDB does not throw errors when deleting non-existent items.

This PR is now ready for review.

@neatbyte-vnobis
Copy link
Collaborator Author

@adrians5j Please review this.

@leopuleo leopuleo self-requested a review January 5, 2024 14:04
Copy link
Contributor

@leopuleo leopuleo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @neatbyte-vnobis!
I left some comments, but overall, it looks good.

return formStats;
},
async getFormOverallStats(this: FormBuilder, id) {
const [formId] = id.split("#");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, let's use parseIdentifier

const model = this.modelWithContext(formStats);

// Append `#0001` since `formStats` always has only one revision.
const formStatsRevisionId = `${formStats.id}#0001`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, use createIdentifier

OnFormStatsAfterDelete
} from "~/types";

const getFormStatsId = (formId: string) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, let's use parseIdentifier

async getFormStats(this: FormBuilder, formRevisionId) {
const id = getFormStatsId(formRevisionId);

let formStats;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's avoid using let when possible. This method could be refactored this way:

        async getFormStats(this: FormBuilder, formRevisionId) {
            const id = getFormStatsId(formRevisionId);

            try {
                const formStats = await this.storageOperations.formStats.getFormStats({
                    where: { id, tenant: getTenant().id, locale: getLocale().code }
                });

                if (!formStats) {
                    throw new NotFoundError("Form stats not found.");
                }

                return formStats;
            } catch (ex) {
                throw new WebinyError(
                    ex.message || "Could not load form stats.",
                    ex.code || "GET_FORM_STATS_ERROR",
                    {
                        id
                    }
                );
            }
        }

async getFormOverallStats(this: FormBuilder, id) {
const [formId] = id.split("#");

let formStats;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, let's avoid using let when possible.

@@ -305,15 +268,16 @@ export const createFormsCrud = (params: CreateFormsCrudParams): FormsCRUD => {
webinyVersion: context.WEBINY_VERSION
};

let result;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should create the formStats entry only if the form entry is successfully created.

Right now, we are trying to create the formStats entry even when the creation fails, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code inside the try-catch will throw an error if the form is not created. This prevents the formStats section from being executed. The same applies to the next two comments as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, let's throw a meaningful error in case the formStats creation fails, something like:

throw new WebinyError(
    ex.message || "Could not create form.",
    ex.code || "CREATE_FORM_STATS_ERROR",
    {
        form
    }
);

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Errors are already implemented for all formStats CRUD operations.

In the createForm method, if form creation fails, it will throw a CREATE_FORM_ERROR. If it succeeds, the createFormStats method is executed, which, in case of failure, will throw a CREATE_FORM_STATS_ERROR.

Similarly, in the createFormRevision method, if form revision creation fails, a CREATE_FORM_FROM_ERROR is thrown. However, if the form revision is successfully created but createFormStats fails, a CREATE_FORM_STATS_ERROR is thrown.

Lastly, for deleteForm, if form deletion fails, it throws a DELETE_FORM_ERROR. Conversely, if the form is deleted successfully but deleteFormStats encounters an error, a DELETE_FORM_STATS_ERROR is thrown.

@@ -543,17 +514,18 @@ export const createFormsCrud = (params: CreateFormsCrudParams): FormsCRUD => {
auth: false
});

let result;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, see my comment above.

@@ -412,6 +379,10 @@ export const createFormsCrud = (params: CreateFormsCrudParams): FormsCRUD => {
}
);
}

await this.deleteFormStats(form.formId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, we should not delete the formStats entry if the form entry deletion fails.

@m0nikaza m0nikaza added this to the 5.39.0 milestone Jan 9, 2024
@adrians5j adrians5j modified the milestones: 5.39.0, 5.40.0 Jan 12, 2024
@leopuleo leopuleo merged commit 929b9fe into neatbyte/form-builder-hcms Feb 14, 2024
76 of 81 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants