Api Platform - returning data from external API - api-platform.com

I'm currently using API Platform to display some data in Elasticsearch. This works fine, but I now have another feature that I'm looking at.
My application needs to deal with a 3rd-party API that needs to hit an endpoint and return some data.
Within my app, I'd like to be able to hit (/api/logistics/{action} - where action is an endpoint, such as login) and this then hits my app layer and returns data (3rd-party can be re-named)
The API calls to the 3rd party are fine, but I'm unsure how to display the response.
I've seen https://api-platform.com/docs/core/data-providers/ which looks like i can create a custom response.
Do i still need to create an entity/model and configure the #ApiResource() with a controller that uses my Data Provider?
If so, then what do i need to add in my annotation, since I won't have an id identifier
I'm fairly new to API Platform and I've not used the Data Provider functionality before
I will not be storing the data from the 3rd party API, just doing a HTTP call, retrieving the response and hopefully displaying it via Api Platform
Thanks

You are mosly right about the dataprovider. But as the docs page General Design Considerations states, "you have to write a plain old PHP object (POPO) representing the input and output of your endpoint. This is the class that is marked with the #ApiResource annotation. This class doesn't have to be mapped with Doctrine ORM, or any other persistence system."
So no, it does not need to be an Entity, but there must be a class marked with the #ApiResource annotation (but putting it in the Entity folder may help to make the #ApiResource() tag work - or adding the folder of your class in api/config/packages/api_platform.yaml).
For an item "get" endpoint your POPO needs an id. The poperty - or if there is only a getter, the getter - must be marked with the #ApiProperty(identifier=true) tag. Usually the easiest way to make one is by imploding/encoding some strings from the response of the external api call that together are unique for the response and will not change. Your dataprovider will have to explode/decode the id and use the components to make the external api call.
For a "post" operation you need a datapersister instead of a dataprovider. Apip will instatiate and populate your POPO and pass it to the datapersister and from there you can make the call to the external api and return an object as the result. If your object is not the same type of POPO you should specify "output"=TheOutputClass::class or put the operation on the output class and specify "input"=TheInputClass::class (replace TheOutputClass or TheInputClass by the actual class name)
For "put" and "patch" you need both a dataprovider, a datapersister and an id. They can have different input and output classes, see the docs about DTOs.
A collectionoperations with method "get" may seem convenient because you can just pass it any query string but your CollectionDataProvider must return an iterable.

Related

HAL clients or examples of accessing HAL API

Question: Any HAL clients or examples of accessing HAL API with admin-on-rest ?
I got started because HAL was mentioned in the first paragraph of the introduction, but now I'm having trouble finding any examples or anyone else using HAL rest client, so I am winding up for now just writing a bunch of simple findAll repositories on top of the already robust existing HAL API.
Adding a more concise answer here that isn't polluted with my thought process now that I've got it all figured out (for anyone's future reference)... Again assuming the HAL API was made with Spring Data Rest.
The four major keys to this integration are:
Exposing foreign key attributes in your JPA entities, which is required in several places by admin-on-rest #Column(name="parentEntity", updatable=false, insertable=false) private Integer parentEntityId;
Exposing all your entity IDs using RepositoryRestConfiguration.exposeIdsFor( MyEntity.class )
Annotate your repositories as #RepositoryRestResource and have them extend PagingAndSortingRepository<MyEntity, Integer>, QueryDslPredicateExecutor<MyEntity> to expose extremely useful search filters by attribute name (e.g. /api/myEntitys?field1=foo&field2=bar).
When submitting create and save requests with foreign keys make sure to adjust your params.data to include the linked resource (e.g. 'http://myserver.com/api/myEntitys/19') on top of (or in place of, HAL has no use for it) the foreign key you exposed in 1. (e.g. myEntityId=19)
Other small items of note:
use PATCH instead of PUT when updating (you may be able to use PUT if you are more of a hibernate expert and can map your entities better than I can but I had trouble getting it mapped perfectly and HAL's PATCH will take partial entities)
When submitting GET_LIST and GET_MANY_REFERENCE you get the total number of items and pagination parameters from the 'page' section of the response, and you use 'size' and 'page' query params in your API requests. (so, no need for headers and stuff)
To change the default 'equals' filter for any string entries (from 3. above) to a 'contains' filter, you will have to also extend QuerydslBinderCustomizer<QMyEntity> and provide your own customize method in each of your repositories. For example:
default void customize( QuerydslBindings bindings, QChampion champion )
{
bindings.bind( String.class ).first( ( StringPath path, String value ) -> path.contains( value ) );
}
We don't have any examples for HAL specifically. However, the point of this introduction was that admin-on-rest is backend agnostic.
You can create your own custom rest client by following the documentation. Read the code of existing ones for inspiration.
For anyone referencing this in the future, if you happen to be in control of your API through Spring Data Rest you can consider the use of an excerptProjection on every one of your existing repositories that shows an inline version of your entity. This would work if there were absolutely nothing besides admin-on-rest accessing your API.
For my case I am planning on writing a custom projection for every rest resource that has entities and naming it the same thing: "inline". Then in the admin-on-rest restClient, just always asking for the inline projection on every GET_MANY or GET_MANY_REFERENCE request.
This is the best I have at the moment. It's not perfect but for the amount of entities I have it's still many weeks faster than building a CRUD interface from scratch so I highly recommend admin-on-rest.

Do backend web service API's have a view as in MVC?

Lets's say I have a backend web API. It takes in a web request and returns some json. Other services then consume that json and do what they will with it.
My question is twofold:
1) Does this backend web API technically have a view (v in MVC)? My thinking is no, since it doesn't actually display any frontend to the user.
2) Does the JSON object returned represent a model (m in MVC)?
Thanks!
1) Does this backend web API technically have a view (v in MVC)? My
thinking is no, since it doesn't actually display any frontend to the
user.
You are correct, it doesn't really have a View.
The Web API itself is simply going to return some data that was requested or something to indicate to the user that a request was performed properly (e.g. a JSON-formatted object indicating that a user was created, a collection of user objects, etc.)
Although a front-end could call the API and then use that information to render something, the Web API on it's own isn't going to do anything like that.
2) Does the JSON object returned represent a model (m in MVC)?
It can.
Each component of the MVC pattern plays an important role :
Controller - Responsible for things like data access and possibly populating a model.
Model - Responsible for representing the data that was accessed or for some type of operation.
View - Responsible for taking the model that was passed along from the controller and serving it to the user.
In this case, when you hit your Controller, you will likely be accessing some type of data and building a model. This model might use some business logic that you have designed yourself, or it might simply be content that was returned from your data layer, either way, the model, regardless of how it was created merely represents some type of data.
The "Model" is this case can be any type of data that you might decide to pass along to a View. Regardless of how it is serialized, if it is consumable either by a View or some other mechanism, you can think of it as a model.

OData V4 (webapi) patch with NavigationProperty collection breaks deserailization on server

I’m trying to go against a Web API Patch method that accepts a Delta. My entity has a navigational property that is a collection. The domain model is trivial, I have a team (entity) and a collection of members (navigational property).
I’m using HttpClient directly instead of the OData client.
The problem I’m running into is that when I send a patch request with the payload = a Team, the Delta deserialized in my controller is null when I include the navigational property collection in my payload.
Eg (ignore that the missing quotes, this is typed in}:
{ Name: Foo } -> serializes to Delta successfully.
{Name: Foo, Members : [] } -> fails to serialize and Delta is null.
Is this supported? I could not find concrete documentation online about whether you can supply navigational properties as an entire collection on patch (not looking for merge support, just want full replace of the collection.)
I also tried tweaking my client to directly send a Delta payload, but the default JsonMediaTypeFormatter is unable to serialize this into a valid request body (always produces empty Json), and the ODataMediaTypeFormatter throws an exception saying it cannot write an object of type Delta, although I have initialized it with every ODataPayloadKind.
I’m sure I’m either doing something wrong or missing something basic, assuming using Delta + patch with HttpClient is not this challenging.
For OData spec, it says:
11.4.3 Update an Entity
...
The entity MUST NOT contain related entities as inline content. It MAY contain binding information for navigation properties. For single-valued navigation properties this replaces the relationship. For collection-valued navigation properties this adds to the relationship.
That is you can't put the navigation properties in the Patch request content.
From Web API OData, it has these codes and these codes. It means that it doesn't allow to patch navigation property form entity.
However, you can shoot your target by following steps:
Patch the principal entity (team)
Patch the dependent entities (members)
use the $ref to update the link
You can refer to my simple test project.

Can Content Negotiation Be Used to Control Return Types in the ASP.NET WebApi?

We're building a web api whose GET methods return DTOs. We'd like to build it so that, under certain circumstances, these DTOs are stripped of unnecessary properties in an effort to control the volume of data being sent down to the client. For example, when we return one of our email DTOs we sometimes would like the client to specify that it only needs a subject, date and ID and not the body of the email. In other scenarios, of course, the body of the email is needed.
What's the best way in the MVC WebApi to do this? I've looked into MediaTypeFormatters but they seem focused on the format of the data (JSONP, XML) rather than the content.
It sounds to me like you would like to have a custom mediatype.
This could be used in combination with a custom MediaTypeFormatter.
For instance, you could define your own mediatype (this is a bad example of a name):
application/vnd.me-shortform
And then, in your code you can omit filling in the emailbody and let the default formatter format your result.
Or you could write your own MediaTypeFormatter (subclassing an existing one) and register it for your custom mediatype.
Then, in the MediaTypeFormatter you could either through attributes on your DTO or something similar decide that the email body is not necessary and omit having it as part of the result.
Mark Seeman on Vendor Media Types should give you a good starting point.

spring - difference between request.setAttribute and model.addAttribute?

can any one tell me the difference between request.setAttribute and model.addAttribute in spring web app?
The difference is, that Model is an abstraction. You could use Spring with servlets, portlets or other frontend technologies and Model attributes will always be available in your respective views.
HttpServletRequest on the other hand is an object specific for Servlets. Spring will also make request attributes available in your views, just like model attributes, so from a user perspective there is not much difference.
Another aspect is that models are more lightweight and more convenient to work with (e.g iterating over all attributes in a model map is easier than in a request).
Request V/s Model
where request could get attributes through getAttribute("") method. normally it is used for getting information from defined attributes and used inside the method for performing the different operations. So Basically Request used for input.
Just like Request, the model provides addAttribute("","") method, Through this model, we could make the object and storing data inside model object and deploying it on result Server Page.Basically it used in storing input data which is provide by us and storing for some time.

Resources