-
Notifications
You must be signed in to change notification settings - Fork 25
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
feat(cleanup): add bulk resource deletion script for integration tests (DEVX-433) #815
base: master
Are you sure you want to change the base?
feat(cleanup): add bulk resource deletion script for integration tests (DEVX-433) #815
Conversation
|
) | ||
console.log(`Deleted ${productTypes.body.results.length} Product Types`) | ||
|
||
// Delete all standalone products (not tied to a product type) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, can you clarify this point - I thought products need always to be tied to a product type? https://docs.commercetools.com/api/projects/products#product
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cneijenhuis Thanks for pointing that out! Yes, you're right, products are always tied to a product type. However, when deleting a product, we don't need to reference the product type explicitly in the deletion process. In this case, I'm fetching all the products and deleting them directly. The link to the product type is part of the product’s metadata, but it doesn’t need to be called out specifically for the deletion to happen. I focus on unpublishing and then deleting the product itself.
Let me know if that makes sense!
taxCategory.id, | ||
taxCategory.version, | ||
async () => { | ||
const productsReferencingTaxCategory = await apiRoot |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not following this for several reasons:
- At this point, we should have already deleted all products - why should we double-check here?
- Why are you trying to duplicate the "safe deletion" logic here? If it isn't safe to delete, the API should refuse to delete (and you should react to the error). E.g. for tax categories, shipping methods also reference them, and the API will refuse the request. Isn't it better to react to that error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- You're right that by the time we get to tax categories, we should have already deleted all products, and ideally, there wouldn't be any remaining references. The reason I added that check was to be extra cautious in case anything slipped through or there were edge cases where products might still reference a tax category. It’s more of a precaution to prevent unexpected errors during the cleanup.
- As for duplicating the "safe deletion" logic, I agree...it’s better to rely on the API’s error handling when trying to delete something that’s still referenced, like in the case of shipping methods referencing tax categories. I can adjust the code to simply handle those errors directly rather than pre-checking, which will make the process cleaner and more efficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@evansinho I would recommend to delete straightaway and not checking.
Reasons:
- At this point products are all delete. If not, then it should fail and you should investigate why. It could be an error in the script and then we have to fix it. If it's a platform error, then we should report it.
- Checking this requires extra request/response. This script could be used as a reference for customers, so we should avoid doing unnecessary actions.
Please check also for other resources that use safeDeleteResource()
.
Nice job 👍🏽 I was looking at the jira ticket and I could see that other methods were also hinted. I think that aside this standalone script solution, we should also have in addition test We created the resource we needed for the test and immediately delete the resource(s) after the tests finished running. This way we are always sure of a clean slate anytime we are running the integration tests. What do you think? |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #815 +/- ##
===========================================
- Coverage 93.07% 71.35% -21.73%
===========================================
Files 25 26 +1
Lines 289 377 +88
Branches 14 17 +3
===========================================
Hits 269 269
- Misses 20 108 +88
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
@ajimae Thanks for the feedback! 👍🏽 I completely agree. Having the beforeAll and afterAll hooks for the test suites to handle resource creation and deletion will ensure a clean slate for each test run, making the process more robust. I'll create a new branch and start implementing this approach alongside the standalone script solution. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job 🔥
Since this is part of our SDK and we might later reference our customers how to do the proper clean up, I added some comments how to improve the script in general.
.withId({ ID: resourceId }) | ||
.delete({ queryArgs: { version } }) | ||
.execute() | ||
console.log(`Deleted ${resourceType.slice(0, -1)} with ID: ${resourceId}`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the resource is deleted, you cannot do anything with the ID outputted here, so I would recommend removing this log.
Rather than this log it's more useful to output a summary in the end of the script - which resources were deleted and how many successfully and how many errors (maybe how long). Also output some kind of counting for long running deletion process - e.g. every 100 resources output Deleted ${count} ${resourceType.slice(0, -1)}
. This way user has a way to follow the execution.
In general we should minimize logging things you cannot act upon - if it's info that sth is successfully deleted, just log the summary in the end of the whole process. On the other hand, the errors should be logged immediately as you have it here. Like this you could see the errors immediately and they're not lost in many logs.
// Function to unpublish and delete a product | ||
const unpublishAndDeleteProduct = async (product: Product) => { | ||
if (product.masterData.published) { | ||
console.log(`Unpublishing product: ${product.id}`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this and write instead a one-line summary in the end of the deletion process like I suggested above.
await deleteProductsForProductType(productType.id) | ||
}) | ||
) | ||
console.log(`Deleted ${productTypes.body.results.length} Product Types`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This log is not correct. In the lines above only products were deleted, product types were not deleted.
Actually product types are not deleted anywhere.
const productTypes = await apiRoot.productTypes().get().execute() | ||
await Promise.all( | ||
productTypes.body.results.map(async (productType) => { | ||
await deleteProductsForProductType(productType.id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not necessary to fetch products per product type if we're going to delete all products. Just fetch all products without where query and delete. This could save some requests/responses in case there are less products.
|
||
// Function to delete all products in the system | ||
const deleteAllProducts = async () => { | ||
const products = await apiRoot.products().get().execute() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't work if there are more than 20 (default limit number) products. You need to use process()
on the client to iterate through all the products. (
export function process( |
Additionally raise the limit to maximum 500 so there are less requests/responses in total (
limit: 20, |
console.log( | ||
`Found ${products.body.results.length} products for product type ${productTypeId}...` | ||
) | ||
return Promise.all( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not recommended to use Promise.all
with requests to commercetools. Rather use Promise.map and limit the concurrency to some lower number like 20. The reason is CTP cannot handle well the deletion and deletion usually triggers some backend work as well. I know this because I did Promise.all
as well and caused the whole platform outage in the past 😅
taxCategory.id, | ||
taxCategory.version, | ||
async () => { | ||
const productsReferencingTaxCategory = await apiRoot |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@evansinho I would recommend to delete straightaway and not checking.
Reasons:
- At this point products are all delete. If not, then it should fail and you should investigate why. It could be an error in the script and then we have to fix it. If it's a platform error, then we should report it.
- Checking this requires extra request/response. This script could be used as a reference for customers, so we should avoid doing unnecessary actions.
Please check also for other resources that use safeDeleteResource()
.
This PR introduces a script to handle the bulk deletion of various resources, ensuring the system stays clean and within resource limits. Key features include: