Open texttechne opened 1 year ago
Hey @texttechne thanks for the input. I had a quick look through odata2ts, not enough to fully understand it, but I can see some key differences in the focus between projects.
I am sure to miss a lot of things, so please fill in any features which are available in odata2ts and not in magic-odata, or features which are available in both
As you said, odata is a massive set of features, and it is not only clients that implement a subset of functionality, but also servers. My guess is that the flavor of the client will depend on which OData server was the primary influence for the project, and that odata2ts and magic-odata have been influenced by different server implementations
@texttechne if you would like, there is an odata $metadata file buried in magic-odata that handles a lot of edge cases. Feel free to grab it if you would find it useful for TDD https://github.com/ShaneGH/magic-odata/blob/main/tests/magic-odata-tests/code-gen/namespaces/namespaces.xml
Hey @ShaneGH, you're definitely right: different server technologies is the main factor which drives the different implementations. I live in the SAP and you in the Microsoft world... actually a brilliant point to join forces 😀
Ok, here is my try of a comparison.
content-type: text/plain
is probably required?)/People/Description
)Currently, I'm working on $value
, primitive properties and maybe $count.
My main problem with some of the advanced querying features ($root, $this, ...) is that I'm lacking a test server which supports these features. The SAP stuff is quite limited in that respect and the official implementations I know (TripPin, OData, Northwind) also don't support such stuff. And without any way to at least test this functionality manually I won't implement it.
And some of those features like parameter aliases are actually not needed => see querying philosophy
For completeness' sake, some stuff you've mentioned missing has been implemented:
There's one main difference: both implementations approach querying differently.
odata2ts is heavily inspired by Java tools like QueryDsl or jOOQ which generate "query-objects" to provide filter functionality. So the odata2ts query is completely type-safe and abstracts away all OData intricacies (which have become less with V4):
odata2tsClient.navToPeople().query((builder, qPerson) =>
builder
.filter(
// lastName will only offer string based operations and requires string as argument
qPerson.lastName.contains("x"),
// age as number property only accepts numbers
qPerson.age.gt(18)
)
.expanding("trips", (tripsBuilder, qTrip) =>
tripsBuilder
.select("budget")
.orderBy(qTrip.budget.desc())
.top(1)
)
)
magic-odata follows a more generic approach as far as I can judge:
odataClient.People
// the person object is generated right?
.withQuery((person, { $filter: { containsString, eq } }, params) =>
containsString(person.LastName, "x")
// is the value comparison for numeric prop a string or a number? i guess string because it's generic?!
.and(eq(person.Age, "18"))
)
// sorry, here I'm lost: how do I specify the expanding now?
So, magic-odata relies on the knowledge of the user when building queries: containsString
can be combined with any property, e.g. person.Age
, so that the user needs to know the type of the property in order to choose the right operators.
odata2ts keeps two aspects open for extension:
Two HTTP client implementations are provided (Axios, JQuery) but it should be easy enough to implement any other technology according to the API. This approach was chosen since some advanced features should be handled by the HTTP client and not backed in into odata2ts:
The other main concern is data types. When confronted with something like Edm.DateTimeOffset
it should be up to the user to choose which type it should be converted to: keep it as string, JS Date, Moment / Luxon, ...
This is what converter-api and the converter packages achieve.
@texttechne emailed you
@texttechne all of this makes sense, cheers. A lot of the stuff you have mentioned is available, just in a slightly different way to odata2s. The more we talk the more I see the equivalence of these two packages
Regarding your issue with test servers and $root and $this, my philosophy on it has been:
Regarding extensibility, magic-odata takes a much more low level approach. Rather than come up with a list of things that could be extended, magic-odata goes with a middleware like approach with interceptors available at all levels of http request generation
This allows things like headers, caching, auth and even http client types to be completely customizable either globally or per request
Specifically regarding $value/$count
accept
header for these queriescontent-type
headers and can process json. Without content-type
a response is considered a string. This case is not possible to model with typescript in a strict mannerOther parts of the spec like, http status codes, also introduce concepts which are not possible to model in typescript (they are not known at compile time), and so are also considered out of scope (for now, although with a long enough time frame, this will be addressed)
Regarding the code snippet of magic-data (odataClient.People.withQuery(....
)
// the person object is generated right?
// is the value comparison for numeric prop a string or a number? i guess string because it's generic?!
eq
must be the same type as the first. Assuming that age is a number, the code should look like: .and(eq(person.Age, 18))
// sorry, here I'm lost: how do I specify the expanding now?
withQuery
can be a single item (e.g. the filter specified in the code snippet) or an array of items, where you can return multiple query elementsRegarding data types, care has been taken to fully support all data types from a query point of view
(caveat1: not including geography and geometry, caveat2: odata2ts#106 is also an issue in magic-odata).
From a response data type point of view, magic-odata sticks to json representations of Edm
types by design, and considers something like luxon out of scope. Again, parsing luxon dates can be achieved with low level extensibility (above) if required. Personally, I would love to get a feature request to do something different here, but I can't see it happening
Regarding some misc points not covered above.
Hi @ShaneGH, fruitful discussion!
Regarding your issue with test servers and $root and $this, my philosophy on it has been: allow for experimentation
You're right there and you do make some really excellent points. I'll switch my philosophy there => convinced! 😁
And it should be clear by now, that both libs implement nearly the same feature set. I'll try to do a high-level comparison later on.
Regarding the data types: this has been a major concern for odata2ts
, hence the converter-api
package. When handling the response you will present your users with the correct typing. But if I want to use a different data type in my code - which is more like a necessity when it comes down to V2 data types, e.g. Edm.DateTime
(\Date(....)\
) - then the generated models get out-of-sync, so to say. Doing this part in the interceptor is, of course, achievable, but your users don't have the correct types and need to create them on their own.
Taking this one step further and this is my holy grail for the project (#107), when $selecting or $expanding the reponse structure is shaped. Since this information is readily available in the query builder it should get reflected in the typing of the response...
Hi, I'm really unsure if I should open this ticket, but here I go. I'm the author of odata2ts. And it actually does what
magic-odata
does. Have you considered this as alternative?Don't get me wrong: It's always cool to have alternatives and there also exist quite different reasons to have them. But since I know how long the road is to generate a full featured OData client (nearly impossible :wink:)...
Anyways, I hope I don't step on your toes by writing this.
But then again, I'm curious and if you have considered it as alternative and wanted to have a different tool, then I would be highly interested in those reasons.