Skip to content

Conversation

alannadolny
Copy link
Collaborator

No description provided.

@alannadolny alannadolny self-assigned this Sep 5, 2025
@github-actions github-actions bot added the domain: backend Changes or discussions relating to the backend server label Sep 5, 2025
@ewaterman
Copy link
Member

can you add some context to the PR? How do these new APIs help with e2e tests? Are they going to call the APIs? Why couldn't e2e tests use the existing non-API importers?

@alannadolny
Copy link
Collaborator Author

@ewaterman, I wanted to add some context after resolving the issue that I am facing in the e2e repository. Those endpoints are created because of the need to improve data creation when we need to handle a lot of rows at once. It's needed in some tests that Katarzyna has to do in the future. It's done using an API to improve the performance of that action. When there is a need to do it more frequently, I mean resetting the data through import before every test, then this approach will be much faster.
The main idea of that is to have a CSV file in the e2e repository with all of the rows that are imported while setting everything up. You can take a quick look here if you want: openboxes/openboxes-e2e#61
For now, tests that I didn't touch are failing, and I am trying to find the root cause of that. After a long investigation, I didn't come up with any idea, so I wanted to check if that issue is related to my local configuration. So I created this PR and the second one in the e2e repository to run the tests here. But the setup that I created in the e2e repository needs those endpoints to import the data.

}

if (!product.save(flush: true)) {
Product savedProduct = product.save(flush: true)
Copy link
Member

Choose a reason for hiding this comment

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

nitpick: I think it'd still work the way it originally was and so this assignment to savedProduct might not be needed. You can still call importedProducts.add(product) later and I believe it'd contain the saved product. I'm not 100% sure though

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You're right, started thinking in JavaScript way on the backend 😄

* Inventory API endpoints
*/

"/api/locations/$id/inventories/import"(parseRequest: true) {
Copy link
Member

Choose a reason for hiding this comment

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

why not /api/facilities/$facilityId/... (instead of /locations) like we've been doing for other APIs?

Also is the parseRequest required?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I can see that we mixed /facilities vs /locations (16 usages vs 14 usages), but I am going to follow the facility pattern

transactionDate(nullable: false,
validator: { value -> value <= new Date() })
validator: {
value -> value <= new Date(System.currentTimeMillis() + 1000)
Copy link
Member

Choose a reason for hiding this comment

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

why is this needed? With this change you can now make transactions 1 second in the future. I don't think we want that

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In the importData method, the baseline date is the current date, and the adjustment date is the baseline date + 1. When the import happens quickly, the adjustment date is in the future, so the validation fails. The logic in importData doesn't seem right to me in that context. I am going to subtract one second while creating ImportDataCommand, unless you have nothing against creating a baseline transaction with the date -1s, and the adjustment just on the transaction date.

}

return inventoryService.findAndUpdateOrCreateInventoryItem(product, lotNumber, expirationDate)
return inventoryService.findAndUpdateOrCreateInventoryItem(product, lotNumber, expirationDateRaw)
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: I personally don't see this refactoring as cleaner since it now needs to call inventoryService.findAndUpdateOrCreateInventoryItem all over the place. If you're really committed to not using else if conditions then I suggest doing this instead:

Date expirationDate = parseExpirationDate(expirationDateRaw)
return inventoryService.findAndUpdateOrCreateInventoryItem(product, lotNumber, expirationDate)


Date parseExpirationDate(Object expirationDateRaw) {
    if (expirationDateRaw instanceof String) {
        Date date = EXPIRATION_DATE_FORMAT.parse(expirationDateRaw)
        Calendar calendar = Calendar.getInstance()
        calendar.setTime(expirationDate)
        return calendar.getTime()
    }
    if (expirationDateRaw instanceof Date) {
        return expirationDateRaw
    }
    if (expirationDateRaw instanceof LocalDate) {
        return expirationDateRaw.toDate()
    }
    return null
}


return parser.collect { record ->
record.toMap().collectEntries { k, v ->
[(toCamelCase(k)): v]
Copy link
Member

Choose a reason for hiding this comment

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

Why do we want to modify the input data? What does it give us?

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 headers of CSV that we are using are like "Product Code", etc., so this is parsing it to "productCode" to work with the import method

Copy link
Collaborator

Choose a reason for hiding this comment

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

What if someone mistypes Porduct Code? Will it try to map to porductCode, which would later result in some potential issues? Or there will be an exception meaningful to the user, or will he be stuck trying to figure out why his file is not being imported?

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 importData method will throw an error in the same way as it is done on the import on Grails pages

Copy link
Collaborator

Choose a reason for hiding this comment

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

ok


private Location parseBinLocation(String binLocationName, Location parentLocation) {
if (binLocationName == null) {
if (!binLocationName) {
Copy link
Member

Choose a reason for hiding this comment

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

What about if we have a bin location with a "" name? I know we've had issues with blanks vs nulls before so I'd be weary of treating them both as equivalent to null.

Nvm I checked and locations cannot be blank so this is fine.

Copy link
Collaborator

@awalkowiak awalkowiak left a comment

Choose a reason for hiding this comment

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

Overall lgtm, but I have a question in one of the threads

@alannadolny alannadolny merged commit 27adf69 into develop Sep 8, 2025
3 of 5 checks passed
@alannadolny alannadolny deleted the OBPIH-6969 branch September 8, 2025 11:09
Copy link

sentry-io bot commented Sep 8, 2025

Suspect Issues

This pull request was deployed and Sentry observed the following issues:

Did you find this useful? React with a 👍 or 👎

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
domain: backend Changes or discussions relating to the backend server
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants