AEM - How to retrieve model-json in OSGi component - aem

Inside of an OSGi component / service I'd need a JSON-representation of a resource (pages, CF etc) exactly like retrieving it via Sling model selector (resource.model.json).
Unfortunately being inside an OSGi component or service, there's no (sling) request object available.
Is there a way to get the json representation (with all the component's model exporters) without creating a http-request to localhost?

that's not a problem as long as you have access to the resource.
First you need to make sure that your Model can deliver the json via a method call. See Get .model.json as String for an explanation on how to do this.
If you are done with that, use the ModelFactory to "getModelFromResource". This will create an instance of your SlingModel for the given resource. Just call the method you created before to get your json.
See https://sling.apache.org/apidocs/sling10/org/apache/sling/models/factory/ModelFactory.html
Your model should probably have adaptables= {Resource.class} - if you adapt from Request there might be trouble ahead.
HTH,
OliG

Related

REST Service Type if no Data Flow is Involved?

If I have to consume a service that does not involve data flow between me and the application, what is the most appropriate call type to use? (GET vs POST vs PATCH vs PUT) for the service I am trying to invoke?
The backend however will do couple of modifications to the backend data in datastore and inturn POST it to another backend. However this in not the level of abstraction I look at when I invoke my service. I just want the "process" to be done and how the backend service does it is not material.
In this context how do I denote the "service" I am trying to consume.
Is it a "GET" service as I am getting a service done? This is all very confusing as these verbs are all data-centric and not process-centric.
Since your server does some manipulation in data on the backend, I think you should use PATCH and the service should return with code 204 No Content.
It's pretty much clear, that you need to use either PUT or PATCH. But what's the difference?
The difference between the PUT and PATCH requests is reflected in the way the server processes the enclosed entity to modify the resource identified by the Request-URI. In a PUT request, the enclosed entity is considered to be a modified version of the resource stored on the origin server, and the client is requesting that the stored version be replaced.
With PATCH, however, the enclosed entity contains a set of instructions describing how a resource currently residing on the origin server should be modified to produce a new version. The PATCH method affects the resource identified by the Request-URI, and it also MAY have side effects on other resources; i.e., new resources may be created, or existing ones modified, by the application of a PATCH.
So that's why I think PATCH will be more suitable for your case.
The REST- Methods are very clear in there meaning, not depending on where you send it and whats happening there.
Get is getting something - to ask for some data.
POST is for creating data, PUT is for manipulating existing data.
If you really dont know what is involved at the backend i would prefer PUT since this for me sounds most abstract.
See the definition here:
http://www.restapitutorial.com/lessons/httpmethods.html
Since you are invoking a process and not really transfering the state of the resource in any way, I think you are simply not dealing with a REST service.
Given that that's the case, I think the most appropriate HTTP method is simply POST. PATCH is wrong because with PATCH you are supposed to send a message that is intended to manipulate the resource that lives on the target URI. As you said, you are really dealing with a process here which means that:
The URI really is not all that meaningful.
You're not manipulating resources.
You can't guarantee idempotence.
Here are some snippets from the HTTP specification that relate to this:
The POST method requests that the target resource process the
representation enclosed in the request according to the resource's
own specific semantics. For example, POST is used for the following
functions (among others):
o Providing a block of data, such as the fields entered into an HTML
form, to a data-handling process;

Configure SAP Gateway service to ignore properties from payload that are not defined

The scenario is simple: I have a list of products and on frontend I create a property on the fly in the oData model ("EditMode"). I use this property only for frontend to enable/disable some input fields.
When I perform an update(POST) the gateway request faild (400-bad request) because "EditMode" is not defined on the Product entity.
How can I configure the gateway to ignore the properties that are not defined and take only what it needs from the payload?
It would be an overhead to delete this property from the oData model before sending the request, it will also affect the UI... :(
Thanks!
I think it's a very bad approach sending properties to the server that are not part of the data model. The OData 4.0 spec says (although SAP GW is still OData 2.0):
6.2 Payload Extensibility
OData supports extensibility in the payload, according to the specific
format. Regardless of the format, additional content MUST NOT be
present if it needs to be understood by the receiver in order to
correctly interpret the payload according to the specified
OData-Version header. Thus, clients and services MUST be prepared to
handle or safely ignore any content not specifically defined in the
version of the payload specified by the OData-Version header.
SAP GW "handles" unexpected properties by cancelling the request and sending a bad request response back. I believe there is no option to change this behavior and it would also kind of break "OData".
I assume you are using SAPUI5 on your frontend. There are many ways how you can achieve what you actually want - I'm very sure about this. But changing the "real" data, i.e. by adding an "additional" property was never needed in my cases. One way would be to bind the editable property of your controls to something like
"{view>/editmode}"
As you can guess this is a view model, also referred to as the "view model pattern". It only means you create a JSONModel in the controller (i.e. in the onInit) and and then call
this.getView().setModel(oModel, "view");
Whenever you want to disabled/enable editing to the set of controls just call this once:
var bEditable = ...; // true or false
//...
this.getView().getModel("view").setProperty("/editMode", bEditable);
Another option would be to have 2 different views/targets, one for editMode and one for display mode. If you want to follow the Fiori guides I think you should use this option. It's up to you...
Those options assume that you use exactly one flag to make "all" controls either editable or not.
If you you need some additional advice make sure to post some code to better illustrate your issue.

How to handle a POST method that generate multiple resource with REST?

I am building my first REST API. I could do most of my queries without any problem but now I encountered a use case I don't know how to solve.
Here is the use case.
I submit a dataset to the API, the dataset is then stored in database (this part works as intended). When stored in the database, it creates different resources due to business rules.
So now I don't know how to inform the user what is the location of the newly created resource since I could have more than one.
I read this Can the Location header be used for multiple resource locations in a 201 Created response? which tells me that only one location header is allowed.
Should I rethink my POST method? Should I use a different way to acknowledge the user where are the resources?
Yes, the Location header requires a single identifier. It's intended for one resource you should follow in order to complete the request according to some predefined semantics.
You can use the Link header instead. Then you can have multiple URIs. Check the RFC 5988 here for a few examples and don't forget to document it properly.
As an alternative, keep in mind that the semantics of the POST method are determined by you, so there's nothing wrong with returning the list of links in the response payload, as long as the resource format allows it in some way and it's documented.

Loopback.io and CouchDB connector

I am trying to explore the opportunity to build a connector for CouchDB for Loopback.io.
I know CouchDB has a REST interface but - for some reason - when putting the baseURL of my Couch local server into a Rest connector in Loopback, I get an error back on some headers missing in the request from Couch.
Since some useful functions could be added to exploit views and so on, I am exploring the loopback-connector-couchdb creation.
So easy question is: what are the methods that a connector needs to implement to map exactly to the standard API endpoints offered by Loopback.io for a model?
Basic example:
POST /models (with payload body) --> all good on the "create" function of the connector
DELETE /models/{id} --> I get an error saying that the destroyAll function is NOT implemented (correct) but the destroy function IS implemented instead...
what is the difference between HEAD /models/{id} and GET /models/{id}/exists in terms of the functions called?
I try to verify the existence of the model created (successfully) in CouchDB via ID and use GET /models/{id}/exists and instead of having the function "exists" called in the Connector, another function called "Count" is called instead.
It is as if some but not all functions are mapped to the connector (note, I am not using the DataAccessObject property of the connector, as that seems to be more for additional methods, so to speak... and one of the methods does work!)
...I am confused!
Thanks for any guidance. I am trying to follow this, but i can't easily map standard API endpoints to the minimum functions of the connector (see point 2 above, for instance)
Building a connector - Loopback.io documentation
I would suggest playing with the API explorer to figure out your endpoints.
Create a sample LoopBack project via slc loopback
Create some models via slc loopback:model
Start the app via slc run
Browse to localhost:3000/explorer
In there you can see all the endpoints that are automatically generated by LoopBack. Like if you click the GET endpoint for a model, it will show the query as GET /api/<modelname>.

Unable to send a proxy object to Server

I have a value proxy object in my client side which I have created using a Request Context. I use the same request context to save that object. Till here its fine.
Now there is a new requirement where I need to send the same object to the server (for different purpose) to the server before the actual save in the flow. How can I do that?
I tried to create a new request context and then send object with that, but I got an error saying thats not allowed.
Thanks.
You have to duplicate/clone it. As it's a ValueProxy it shouldn't be a problem. The issue is that there's no easy and clean way to clone a proxy.
The cleanest solution is to use AutoBeanUtils.getAutoBean and then an AutoBeanVisitor to visit each property and set it on another proxy.
An easier way is to serialize the proxy into a ProxyStore and deserialize it, which will create a distinct proxy.