diegomvh / angular-odata

Client side OData typescript library for Angular
https://www.npmjs.com/package/angular-odata
MIT License
46 stars 14 forks source link

Inline annotations support for parsing metadata xml documents? #81

Open tillroessner opened 1 year ago

tillroessner commented 1 year ago

Hello, i was wondering if the ODataMetadata class has any support for inline annotations. In the TripPin Example Service (https://services.odata.org/V4/(S(ct10fpe4d4afxt4kd53utjpa))/TripPinServiceRW/$metadata), there are some inline annotations, for example in the "Person" EntityType:

<Property Name="UserName" Type="Edm.String" Nullable="false">
     <Annotation Term="Org.OData.Core.V1.Permissions">
          <EnumMember>Org.OData.Core.V1.Permission/Read</EnumMember>
     </Annotation>
</Property>

When i am looking at the metadata object provided by for example oDataClient.metadata().fetch(), the ODataMetadata contains all other information from the xml, such as external annotations, but the inline annotations are missing.

Is there some configuration i would need to change to have them, or are they just not supported? Or is there maybe another way to access these annotations? Maybe you can clear this up for me a little, Thanks!

diegomvh commented 1 month ago

Hello @tillroessner,

Apologies for the delay, but your question wasn't my focus at that time. A few days ago, I started improving the parser that works on the metadata, and indeed, these features were not implemented; the parser did not have support for inline annotations.

Today, this is implemented in a basic and rough manner but available for the next version. Here is an example, which will likely change because I don't like how verbose the implementation is.

const username = metadata.schemas
  .find(s => s.namespace === "Microsoft.OData.SampleService.Models.TripPin")?.entityTypes?.find(e => e.name === "Person")?.properties?.find(p => p.name === "UserName");
console.log(username?.annotations?.find(a => a.term === "Org.OData.Core.V1.Permissions")?.members?.map((m: any) => m.text));

On the other hand, the ability to work well with the metadata led the development toward an alternative that had been on my mind for some time: what if I use the metadata to feed the internal parser generator of the library? This way, when obtaining the data from the server, it can be processed according to the metadata specifications, and it would even be possible to work with collections and models dynamically.

const api = this.odata.apiFor("TripPinDynamic");
api.metadata().fetch().subscribe(metadata => {
  api.populate(metadata); 
  const entitySet = api.entitySet<Person>("People");
  const schema = api.structuredType("Person");
  const person = entitySet.entity("scottketchum");
  person.query(q => q.expand(({e, t}) => 
    e()
    .field(t.Trips, f => f.expand(({e, t}) => e().field(t.Photos)))
    .field(t.Friends, f => f.expand(({e, t}) => e().field(t.Friends)))
  ));
  person.fetchModel().subscribe(console.log);
});

I am working on this now, and I think it could be interesting for those who use the library and can obtain the metadata. After obtaining the metadata and populating the API, it would be possible to interact with the API in a quite interesting way.

This is something I am working on, and it may require several more tests and updates... for now, the idea is presented, and the code is published. The examples are valid, and some development can be seen in the example project.

https://github.com/diegomvh/AngularODataEntity/blob/c13edeb7afe83ffedf3633183a0b345c75dc7c85/src/app/app.component.ts#L471

Regards, and apologies once again.