-
Notifications
You must be signed in to change notification settings - Fork 3
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
1571: Import stores backend #1606
Conversation
…ckend and frontend
…d components, add dryRun option
@@ -8,9 +8,6 @@ import bayernConfig from '../../project-configs/bayern/config' | |||
import CreateCardsButtonBar from './CreateCardsButtonBar' | |||
|
|||
jest.useFakeTimers() | |||
jest.mock('csv-stringify/browser/esm/sync', () => ({ | |||
stringify: jest.fn(), | |||
})) |
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.
moved this to the jest.setup for all tests using it
31424f6
to
05e9a3e
Compare
05e9a3e
to
cf835cb
Compare
...nd/src/main/kotlin/app/ehrenamtskarte/backend/stores/importer/nuernberg/steps/DownloadCsv.kt
Outdated
Show resolved
Hide resolved
...c/main/kotlin/app/ehrenamtskarte/backend/stores/webservice/AcceptingStoresMutationService.kt
Outdated
Show resolved
Hide resolved
...c/main/kotlin/app/ehrenamtskarte/backend/stores/webservice/AcceptingStoresMutationService.kt
Outdated
Show resolved
Hide resolved
...rc/main/kotlin/app/ehrenamtskarte/backend/stores/database/repos/AcceptingStoresRepository.kt
Outdated
Show resolved
Hide resolved
...rc/main/kotlin/app/ehrenamtskarte/backend/stores/database/repos/AcceptingStoresRepository.kt
Outdated
Show resolved
Hide resolved
/** Returns null if string can't be trimmed f.e. empty string | ||
* Removes subsequent whitespaces | ||
* */ | ||
fun String?.clean(removeSubsequentWhitespaces: Boolean = true): String? { |
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.
nit-picky:
if we call this function with removeSubsequentWhitespaces = false
, we get the same effect as trim().
so I'm not quite sure why this extra branch of logic is needed?
when we need only trim() why not just call trim() ?
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 adjusted this function to return null for empty string and added removeSubsequentStrings false
if it is used for the csv import via administration
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.
Was it on purpose to remove replaceNa()
?
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.
can you give me an explanation about what that notes
should be that will be replaced with this function. i couldn't find any good example for that.
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 readded replaceNa()
just in case to not break anything.
It would be good to add an example in the tests @steffenkleinle. But this can be done is a separate task
I couldn't find much information about that and how a string with such a note looks like
|
||
fun mapCsvToStore(csvStore: CSVAcceptingStore): AcceptingStore { | ||
return AcceptingStore( | ||
csvStore.name.clean()!!, COUNTRY_CODE, csvStore.location.clean()!!, csvStore.postalCode.clean()!!, csvStore.street.clean()!!, csvStore.houseNumber.clean()!!, "", csvStore.longitude!!.toDouble(), csvStore.latitude!!.toDouble(), csvStore.categoryId!!.toInt(), csvStore.email.clean()!!, csvStore.telephone.clean()!!, csvStore.homepage.clean()!!, csvStore.discountDE.orEmpty() + "\n\n" + csvStore.discountEN.orEmpty(), null, null |
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.
if discountDE and discountEN are null, I think the expected description should be null, or?
currently it's "\n\n"
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.
yes i added a check
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.
now if discountDE is null and discountEN is not null ("test description", for example), it's saved like "\n\ntest description".
probably it should be "test description"
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.
addressed this in last commit by using a mutableList
now its even better extendable if we want to add another language for discount someday
...c/main/kotlin/app/ehrenamtskarte/backend/stores/webservice/schema/types/CSVAcceptingStore.kt
Outdated
Show resolved
Hide resolved
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.
Tested normal store import via run config. Works as expected
Is it expected that project admins cannot create users with the role store import? Because only we want to use this feature? If not, users need the option to select the PROJECT_STORE_MANAGER role, when creating a new user.
Add a new file to the other csv resources as an example of the csv import please.
View is disabled for project with disabled upload and visible for nuernberg as expected.
import worked as expected.
@@ -29,6 +31,13 @@ const StoresImportController = (): ReactElement => { | |||
return <StoresImport fields={storeManagement.fields} /> | |||
} | |||
|
|||
const CenteredSpinner = styled(Spinner)` | |||
z-index: 999; |
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.
suggest to use 500 so we could possible put something before in the future.
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.
but what should be before an loading spinner f.e ? even its shown over a modal it should be highest index
...c/main/kotlin/app/ehrenamtskarte/backend/stores/webservice/AcceptingStoresMutationService.kt
Outdated
Show resolved
Hide resolved
...c/main/kotlin/app/ehrenamtskarte/backend/stores/webservice/AcceptingStoresMutationService.kt
Outdated
Show resolved
Hide resolved
yes for the moment the store import is an internal function and not provided to customers. When we decide to sell that function, we have to add the possibility to create users with this role |
34403ec
to
e1f49cc
Compare
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.
Maximale Dateigröße: 2MB
If I try to upload 2Mb file (it's appx 5500 lines), I'm getting an error on the backend side:
[JettyServerThreadPool-33] WARN io.javalin.Javalin - Body greater than max size (1000000 bytes)
io.javalin.http.HttpResponseException: Content Too Large
at io.javalin.http.servlet.MaxRequestSize.throwContentTooLargeIfContentTooLarge(JavalinServletContext.kt:236)
at io.javalin.http.Context.bodyAsBytes(Context.kt:147)
at io.javalin.http.servlet.JavalinServletContext.access$bodyAsBytes$s-1678783089(JavalinServletContext.kt:65)
at io.javalin.http.servlet.JavalinServletContext$body$2.invoke(JavalinServletContext.kt:133)
at io.javalin.http.servlet.JavalinServletContext$body$2.invoke(JavalinServletContext.kt:133)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
at io.javalin.http.servlet.JavalinServletContext.getBody(JavalinServletContext.kt:133)
at io.javalin.http.servlet.JavalinServletContext.bodyAsBytes(JavalinServletContext.kt:134)
at io.javalin.http.Context.body(Context.kt:138)
at io.javalin.plugin.bundled.DevLoggingPlugin.httpDevLogger(DevLoggerPlugin.kt:55)
at io.javalin.plugin.bundled.DevLoggingPlugin.onInitialize$lambda$0(DevLoggerPlugin.kt:29)
at io.javalin.http.servlet.JavalinServlet.writeResponseAndLog(JavalinServlet.kt:114)
at io.javalin.http.servlet.JavalinServlet.handleSync(JavalinServlet.kt:68)
at io.javalin.http.servlet.JavalinServlet.handle(JavalinServlet.kt:50)
at io.javalin.http.servlet.JavalinServlet.service(JavalinServlet.kt:30)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:587)
at io.javalin.jetty.JavalinJettyServlet.service(JavalinJettyServlet.kt:52)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:587)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:529)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1580)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1381)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1553)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1303)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
at org.eclipse.jetty.server.handler.StatisticsHandler.handle(StatisticsHandler.java:173)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
at org.eclipse.jetty.server.Server.handle(Server.java:563)
at org.eclipse.jetty.server.HttpChannel$RequestDispatchable.dispatch(HttpChannel.java:1598)
at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:753)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:501)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:287)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:421)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:390)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:277)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.run(AdaptiveExecutionStrategy.java:199)
at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:411)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)
at java.base/java.lang.Thread.run(Thread.java:840)
@seluianova |
0f55eaa
to
e515ae2
Compare
Uploading 10000 stores take 80seconds. I added a aprox. duration to the alert dialog, for mass uploads to inform the user that it may take a while |
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.
/** Returns null if string can't be trimmed f.e. empty string | ||
* Removes subsequent whitespaces | ||
* */ | ||
fun String?.clean(removeSubsequentWhitespaces: Boolean = true): String? { |
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.
Was it on purpose to remove replaceNa()
?
...c/main/kotlin/app/ehrenamtskarte/backend/stores/webservice/AcceptingStoresMutationService.kt
Outdated
Show resolved
Hide resolved
...rc/main/kotlin/app/ehrenamtskarte/backend/stores/database/repos/AcceptingStoresRepository.kt
Outdated
Show resolved
Hide resolved
...rc/main/kotlin/app/ehrenamtskarte/backend/stores/database/repos/AcceptingStoresRepository.kt
Outdated
Show resolved
Hide resolved
...rc/main/kotlin/app/ehrenamtskarte/backend/stores/database/repos/AcceptingStoresRepository.kt
Outdated
Show resolved
Hide resolved
I would do a final review once you have addressed Steffen's comments if you don't mind, in order to re-test all together, but I think it should be good to go. |
...ain/kotlin/app/ehrenamtskarte/backend/exception/webservice/exceptions/DatabaseIOException.kt
Outdated
Show resolved
Hide resolved
discounts.removeIf { it.isNullOrEmpty() } | ||
return discounts.joinToString("\n\n") | ||
} | ||
fun mapCsvToStore(csvStore: CSVAcceptingStore): AcceptingStore { |
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.
optional:
do we want to handle an error, when csvStore failed to be mapped into AcceptingStore?
it can happen for example if categoryId can't be parsed to Integer, or coordinates cannot be parsed to Double.
(or if some fields are missed)
currently the service returns 500 internal server error in such case.
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 can add an improvement github issue. I think this pr is now big enough :)
5a270ed
to
1246274
Compare
i think i addressed all import ones @seluianova |
nit-picky thought: and vice versa: I'm not pushing to change this behavior right now, just highlighting it for discussion I think at best this behavior between the front and back should be unified |
Absolutely valid input. I think this should be added in the cleanup. Please feel free to add also other improvements or cleanups to that issue |
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.
👍
thanks for the great effort!
I think this should be added in the cleanup
not sure if validation on the backend is related to the clean up though, but maybe I'm missing something.
I think it could just be added to the mutation and it wouldn't affect the old import.
I see it like a separate issue - add the same validation in the backend as on the frontend.
I can create a ticket for that if it sounds reasonable to you.
ah okay now i got it. Yes then please create a separate task |
Short description
As a user i want to import stores accepting stores via csv. The backend part is implemented here
Proposed changes
Side effects
https://github.com/digitalfabrik/sozialpass-nuernberg-data/blob/main/nuernberg-akzeptanzstellen.csv
Note: If you have added the koblenz project to your
config.local.yml
you have to set thispipelineName: BerechtigungskarteShowcase
for koblenz to skip the store import via pipelineTesting
Resolved issues
Fixes: #1571