-
Notifications
You must be signed in to change notification settings - Fork 16
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
relates to C2-2197: setup the command serialization with tests #7
Conversation
111027e
to
1a29218
Compare
1a29218
to
be5193f
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.
Code comments..
@JsonSubTypes.Type(value = InsertOneCommand.class), | ||
}) | ||
@JsonIgnoreProperties({"commandName"}) | ||
public interface Command {} |
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 should be called CollectionCommand
as we need to differentiate between commands that can be used on the /v3/{database}
and /v3/{database}/{collection}
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 we put that after Command so we have a common base for all Commands.
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.
Yea would need to define the hierarchy then on the new class, keeping it like this, we can adapt when we add the DatabaseCommand
s.
src/main/java/io/stargate/sgv3/docsapi/api/model/command/ReadCommand.java
Outdated
Show resolved
Hide resolved
* @param sortExpressions Ordered list of sort expressions. | ||
*/ | ||
@JsonDeserialize(using = SortClauseDeserializer.class) | ||
public record SortClause(@Valid List<SortExpression> sortExpressions) {} |
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 needs proper schema defining this is a array of strings in the swagger
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 have been wondering about this: I thought the intent is for "DocsV3" to be backend for Mongoose drivers but not a general-purpose REST API. If so, would Swagger even be needed/useful?
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 for the target is not HTTP REST.
We also want to avoid putting the JSON encoding attribution on the commands. The commands should just represent the object model, serialisation is seperate.
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.
Here there are few things that we should discuss:
- do you both imply that we don't need swagger ui here? why is that, seems quite handy? how would this be applied to Astra prod where we do need the swagger ui?
- avoid putting the JSON encoding attribution on the commands, what does this mean? is this for going back on the mixins way? so you would rather not have model and serialization in one place, easy to understand and look at, but rather split this into another, effectively not needed, class that you need to search and find to understand how's everything working?
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.
@tatu-at-datastax @amorton Aaron and I kinda agreed to keep the swagger UI for now and try to define the schemas correctly.. We would not only have the benefit of supporting astra-prod requirements, but also gain benefits from the open API spec that can be used in postman, to generate clients or display docs in redoc or other similar tools..
Regarding the JSON encoding attribution, we agreed that we need to think for a day or two about extracting engine out of the http stuff. This would mean having two projects in this repo, one pure engine, another HTTP wrapper around.. IF we do so, the JSON encoding attribution would be done in the HTTP project using mixins.
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.
Yeah I am not dead against Swagger, but also want to help avoid significant effort being spent on something that won't be needed (if that's the case).
Sort of like why GraphQL doesn't have Swagger definitions even though technically speaking one could add definitions (but no one would use Swagger).
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.
On JSON bindings: to me most of annotation metadata is not a form of tight coupling. There are exceptions (specifying custom (de)serializers f.ex), but for basic external naming of properties where needed (overrides with @JsonProperty
) externalization seems like unnecessary work.
There's only a single dependency (jackson-annotations
) package (which itself has no deps), for annotations themselves and any library/framework is free to use or not those (that is: there is no need to depend on Jackson library for parsing/generation of JSON at all; only annotation types). Model itself could be used with other libraries too (but possibly adding annotation/configuration they need).
I can help minimizing need for annotations and maybe avoid most or all mix-ins.
// TODO correct typing for the path, we could use some kind of a common class | ||
// we also need a validation of a correct path here | ||
@NotBlank String path, | ||
|
||
// this can be modeled in different ways, would this be enough for now | ||
boolean ascending) {} |
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.
@amorton Look for suggestions here
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.
We cannot implement sort until we get sort in the core DB next year so I had not thought about it. But this is basically it, there is a list of field and sort order.
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.
Keep like this until further notice..
return null; | ||
} | ||
|
||
// TODO, should we have specific exceptions, or throw generic JSON ones? |
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.
@amorton wdyt, can you have a look on our BusinessRuntimeException
idea in the v2?
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 kept all the exceptions as RuntimeException to be consistency wrong as I was not aware how we wanted to model them. We will need something like that, I would guess there are Syntax errors and InvalidRequest for this sort of area, based on the CQL error states.
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.
Let's discuss and agree on this..
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.
Not being discussed, will create an issue for this.
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.
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 think that there absolutely should be at least one custom exception (base) type beyond JDK provided ones. Can start with a single "document exception" type which can be RuntimeException
if we want to avoid checked exceptions (which seems to be where most Java devs are leaning).
@Schema(description = "Command that inserts a single JSON document to a collection.") | ||
@JsonTypeName("insertOne") | ||
public record InsertOneCommand( | ||
@NotNull |
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 should contain a proper error message..
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.
We need to put the validate() methods back and do validation in there, for a couple of reasons.
- there will be complex validation steps, e.g. check that nothing in an update clause tries to change the _id of a document, or that the data types for an _id document are those we support.
- we want the document library to work outside of a spring boot environment.
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 achieve anything you want as you can easily create your own annotations, validations and rules.
This had nothing to do with spring boot, this is javax.validation
, a framework that helps you validate things easier.. Meaning, that you can still use the framework outside of spring boot or quarkus.
therwise, you can avoid having it, but then create you own validation framework, meaning you need to have vlidations in place, you need a way to validate those, create error messages and translate that to the response with errors.. Wouldn't this be an overkill, especially when we are so fragile about the time?
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.
Agreed to keep the javax.validation
. For now we will let Quarkus validate commands. In the future, we want to integrate this into engine, so that commands are validated no matter how are they created. We have a ticket for validation, so we will do it there: https://datastax.jira.com/browse/C2-2280
} | ||
} | ||
} | ||
"""), | ||
})) | ||
public class StargateDocsApi extends Application { |
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 is related to the bigger naming question but... Isn't it bit confusing to have 2 completely different "Docs API" instances? If we do decide to go with "DocsV3" then at least add V3 in the name here, to tell from our existing "DocsV2".
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.
Package is different, would this be enough? I am happy to hear the suggestions?
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.
Yeah it's different for many uses. Not a big deal but I'd go with just StargateDocsV3Api
. Bit of redundancy in just one place.
I do not propose to use it everywhere, just here.
private ObjectMapper createMapper() { | ||
// enabled: | ||
// case insensitive enums, so "before" will match to "BEFORE" in an enum | ||
return JsonMapper.builder().enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS).build(); |
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.
Do we really want to allow case-insensitive enum values? (I guess question for Aaron)
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 added it for the returnDocument option of the findOneAndUpdate command. While the enum name would be BEFORE or AFTER we would not want to force that in the JSON encoding
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.
One thing to note: instead of globally allowing all Enum
values to be case-insensitive, it is possible to annotate just ones that do. It's with something like:
@JsonFormat(with = [ JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_VALUES ])
public MyEnum value; // on field or accessor/method
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 then you must no forget it on every enum, globally it is imo..
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, true. This would be only if a small number of places allowed this which is sometimes the case.
@JsonSubTypes.Type(value = FindOneCommand.class), | ||
@JsonSubTypes.Type(value = InsertOneCommand.class), | ||
}) | ||
@JsonIgnoreProperties({"commandName"}) |
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'll test this at a later point but I don't think this ignoral is needed.
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 you are correct, simply copied from the poc code and did not give a lot of thought here..
@Override | ||
public SortClause deserialize(JsonParser parser, DeserializationContext ctxt) | ||
throws IOException, JacksonException { | ||
JsonNode node = parser.getCodec().readTree(parser); |
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 can fix this, but better version is
JsonNode node = ctxt.readTree(parser);
import javax.validation.Valid; | ||
import org.eclipse.microprofile.openapi.annotations.media.Schema; | ||
|
||
@Schema(description = "Command that finds a single JSON document from a collection.") |
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 a -1 on swagger annotations like this in the code. We want the Doc API library to be a library like the CQL Native Binary driver, it's with a HTTP/JSON interface is just one use. There could also be gRPC or included as a library in a client code.
I have the same thoughts on sprint boot attributions like @Valid. It limits the library to working in a spring boot environment, which I would like to avoid for core business logic like this.
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.
Again, not sure I am following that you want not to have swagger? And how would this work with astra-prod? I see a benefit of having it, because you can simply start the app, go to swagger and start executing commands.. You don't need postman, collections, client, nothing.. But again let's discuss and agree..
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.
Agreed that swagger annotations in the code make sense since they force programmer to change docs if the code changes.. It also ensures docs and code are merged together.. If we decide to split the engine in the separate project, then we need to see where should the annotation reside.
@Parameter(name = "database", ref = "database"), | ||
@Parameter(name = "collection", ref = "collection") | ||
}) | ||
@RequestBody( |
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.
A Question - I quickly looked at the OpenAPI spec when I was looking at postman, and it looked like we would need to define the whole query API was that a correct assumption ?
Happy for this to be here, we will not be promoting the Doc V3 API as a HTTP API to start with. To start with will be promoting and expecting all calls to come from Mongoose, I'm sure we will later expand to other ORM's and direct calls.
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 did not get this one sorry..
@tatu-at-datastax @amorton Added review fixes, added comments about the discussion points.. I would like to merge this as is and continue, but an approval.. The todos are left for future work.. |
I am +1 for fast merging, fixing things that need fixing later on. |
Why not giving an approval here? Aaron did not provide one as well, and now I have to wait even longer? |
Sorry I wasn't sure if Aaron had concerns to resolve (yes, I know that technically it is possible to add request for change). But I'll approve this now (for tomorrow). |
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.
As per my notes, assuming @amorton has no objections.
Merging this, remaining comments can be pushed to #10 |
Work in progress..
This PR summarizes show we should pull the code from the POC/demo project @amorton created.. There are a lot of things that need attention, so let me try to explain it here:
Command
,ReadCommand
andModifyCommand
are modeled as interfaces, mainly so we can userecord
for the command implisValid
andvalidate
stuff, not needed as we can usejavax.validation
directly (see example on missing document node ininsertOne
)SortClause
is simplest, so I went for implementing that in the deserializing@JsonDeserialize
so no need for custom modules, again modules are usually coming from 3rd party libsMissing peaces:
findOne
command