I'm reading the OpenAPI callback spec, my main sources are
https://spec.openapis.org/oas/latest.html#callback-object (spec)
https://github.com/OAI/OpenAPI-Specification/blob/main/examples/v3.0/callback-example.yaml (example)
The spec (first link) states that the structure should be like:
Operation Object
callbacks: Callback Object
[pattern]: Path Item Object
get: Operation Object
post: Operation Object
etc...
However the example (second link) describes a callback-enabled operation like so (ignored the irrelevant parts):
openapi: 3.0.0
paths:
/streams:
# Code above is irrelevant, just want to keep the structure
post: # Operation Object level
callbacks: # Callbacks Object
onData: # Not sure what this is???
'{$request.query.callbackUrl}/data': # Path Item Object level
post: # Operation Object level
...
My question is where does eiter onData or this: '{$request.query.callbackUrl}/data' come from? According to the spec the Path Item Object should be one level above, and the onData (or the interpolated URL) field has no business being there, one level of nesting should be removed.
According to my interpretation, the example should either look like this if we are following the spec:
openapi: 3.0.0
paths:
/streams:
post: # Operation Object level
callbacks: # Callbacks Object
'{$request.query.callbackUrl}/data': # Path Item Object level
post: # Operation Object level
...
Or the spec should be like this if we are following the example:
Operation Object
callbacks: Callback Object
[name]: Paths Item Object
[pattern]: Path Item Object
get: Operation Object
post: Operation Object
etc...
Operation-level callbacks field is a map where the keys are arbitrary IDs/names and the values are Callback Objects. onData in this example is a key/name/ID of the callback.
Each Callback Object is also a map where the keys are runtime expressions that evaluate to the request URL for the callback, and the values are Path Item Objects that define the callback request format (HTTP method, request body, expected status codes, etc.)
paths:
/streams:
post:
...
callbacks:
# Arbitrary name/ID of this callback
onData:
# Callback Object starts here
# This expression defines the URL to send the callback to
'{$request.query.callbackUrl}/data':
# Path Item Object starts here
post:
...
Here, {$request.query.callbackUrl}/data means that the callback URL is constructed from the callbackUrl query parameter of the parent request, with /data appended to it. For example, if a developer sends a callback subscription request as
POST /streams?callbackUrl=https://myserver.com/webhooks
the callback will be sent to:
https://myserver.com/webhooks/data
Check out the Callbacks guide on swagger.io for additional explanations.
Related
This is my response:
{"token":"eyJ0eXAiOiJKV1QiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiUlNBMV81In0.ezPR0PlML3xO08dEYnJO2QfW8wLc9Hbju1wSqBGM0MEC_YUmsOzQH9X_KvVAH0si7xUVrH74IcLcOlkqGBB5x1ZWQHzh_Zn3ej1JS_n55iXqK1bcOn2dGYjr-yTbIRzxAtUYy8FoV3aEPHwGHh6Ehc3AgOgSSq69ep4CcAUZ38Ga65cd-Aei7dfXMMqtYpHksj6K2TJ3EpFPqqZTfwmLnSomQiWNBC5u6Adkjra5zcQWNRsE2ghcaNQGtMURwjZzeNo4wMWewt1K7CobzS9JPNscK_taOIIhV3vljAf_mRkU7imujdXhchErXoTLD6-cq15txh7xJeo9o1cIm2RDLQ.UCfbgjMC5WsOf_kJsPnQOg.tfvajQljduDdtzwaWaKVxEjr4iEUeo72nS-vag6J35UIdUxdqrQYBixeKjw3vidLN7md2qNRR3fuhzejpkV05DXfyU-bDLa17wn42Ad9L5mId9mNrF9E5oVOGfnrNuoACtCgUp7ntJFBrlhzfNBXJa6AcTOrMSIX5pkXuU4ULhjFw92DqPShbUKCFQFl8UHCmU5u6263t5sSeZmwhmU65fwUeFNnbrMMePYKvRhAOEqe88djreX2j4ErAhBaDQNWVmijtqh32LFHvm_WVX8BmfY5T5-0rfMmRqZXNgesDNEdQRp1FqnSKe9rk22j4DM3y3UiXiMdS2U6eEVD_aeeQZUAqTfS7Q-cWJAxxWQLkrqeMrbLPFD6tfvac_4NQ1sgOG-CNaszkR-_0cwV1c9udQ.9FtyIM8BlR7sQXnO2ILV0CVuhiOy7oxZ8Aol2niJae9apU5T6ff5pynLqsmf6zx1fACne9vW7eNhKfup2LZWUg", "loggedInUserName": "usadm002", "loggedIndomain": "gisus"}
I need to extract token and use in 2nd API
Use JSON Extractor Postprocessor for this purpose. Add the JSON extractor element to the request which returns the above-mentioned response. Then configure it as below:
Name of the created variables: TOKEN (Any name)
JSON Path Expressions: .token
Default Values: NOT_FOUND (The Custom value you want to see by default)
Then you can use the variable in your subsequent requests as ${TOKEN}
I have an OData service which is made in manifest.json
In some services I need to pass some url parameters like ?$expand=XYZ
SO the question is how can I set serviceUrlParams in the run time and not inside of the manifest.json while the OData model has been created by component class.
There seems to be a misunderstanding that parameters such as $expand belong to the serviceUrlParams. No, we need to distinguish between
Metadata URL parameters: metadataUrlParams in model definition. Those parameters will be only attached to the $metadata request which is at the model instantiation.
Service URL parameters: serviceUrlParams in model definition. Those parameters will be attached to all requests.
Data URL parameters:
parameters in binding definition (usually in the view) as mentioned here, or
urlParameters in model APIs such as read as mentioned here.
For $expand, data request parameter is what you have to define in each binding definition whereever it's needed. Then, the respective data will be loaded on-demand.
API Reference: ui5.sap.com/#/api/sap.ui.model.odata.v2.ODataModel
Documentation: ui5.sap.com/#/topic/6c47b2b39db9404582994070ec3d57a2
Hope this will helps.
var oModel = sap.ui.getCore().getModel();
oModel.read("/entityset?$expand=" + dynamicValue,mParameters);
for expand url data use the second parameter of the read function.
oDataModel.read("/yourEntitySet", {
success:jQuery.proxy(this._yourSuccessCallback, this),
urlParameters: {
"$expand": "YourExpandData"
},
error: jQuery.proxy(this._yourErrorCallback, this)
});
the assembly of my API Connect API contains two invokes. The first is calling an internal routing API to get some routing information. The response of this routing API should not be passed to the second invoke.
If I do not configure a 'response object variable' in the invoke of the routing API, the original request body is overwritten and the second API gets the result from the routing API as request body. And if I specify a 'response object variable' in the routing invoke, I can not access the content (json) of this variable in the following steps.
How can I solve this issue?
Thx 4 help.
Rather than relying on reading the request object, you can read from your configured 'response object variable' later on in the flow. For instance, if your first invoke has a response object variable set to 'resp1', you can access the JSON payload using '$(resp1.body)' later on in the flow. Using this technique will allow you to store the response of each invoke in a separate object, avoiding the overwriting issue. These response object variables can be read just like any other context variable in the flow.
For more info, check out these links in the Knowledge Center:
Invoke Policy: https://www.ibm.com/support/knowledgecenter/en/SSMNED_5.0.0/com.ibm.apic.toolkit.doc/rapim_ref_ootb_policyinvoke.html
Context Variables:
https://www.ibm.com/support/knowledgecenter/SSMNED_5.0.0/com.ibm.apic.toolkit.doc/capim_context_references.html
I don't understand this part:
[...] "And if I specify a 'response object variable' in the routing
invoke, I can not access the content (json) of this variable in the
following steps." [...]
Why can't you access the content of this variable in the following steps?
Save copy of the request...
... that you received. What I'd do is always save a copy of the data received in the invoke to a processed variable instead of the (raw) original request.
In your GatewayScript try something like this:
let objRequest = apim.getvariable("request");
let body = null;
Here I recommend you to change the body (if json) to a standard js object
if(objRequest && objRequest.hasOwnProperty("body")){
try{
body = JSON.parse(objRequest.body);
}catch(e){
body = objRequest.body;
}
}
Remember to stringify the complete object before saving it as global variable. Is the only way to store it (because you can only store a string value in this kind of variables)
apim.setvariable("objRequest", JSON.stringify(objRequest));
Retrieve copy of the request...
...that you have saved in global variables you can get it from any other GatewayScript that you want this way:
let objRequest = JSON.parse(apim.getvariable("objRequest"));
Be careful not to assign an existent name to the apim.setvariable(name, value) because if you use "request" as name instead of "objRequest" (or other), you'll be replacing the original request element, and we don't want that to happen.
If you need to set or retrieve the status.code...
...you can do it with:
let statusCode = objRequest.body.status.code;
I am using Restangular to download an object, update it and then attempt to save back to the server using the save method. Here is the code that retrieves the object:
Restangular.one("survey", surveyID).getList().then (
function (response) {
$scope.survey = response[0];
}
);
This sets $scope.survey to a properly "restangularized" object, with the fields that come back from the GET request, along with the methods like save, put, etc.
If I then invoke the following function after making some edits to the $scope.survey object:
$scope.saveChanges = function () {
$scope.survey.save();
};
restangular tries to use the URL /survey/1/undefined for the PUT request (1 is the correct ID for the object).
My survey object doesn't have an id field (it's surveyID instead), and so I suspected this might be the problem. However, replacing the surveyID field with an id field changed the URL to be /survey/1/undefined/1
I have stripped down the object returned by the GET request to be just primitives, and this does not change the situation.
Why is the incorrect route being generated?
II discovered the problem was actually with the REST service; when called with GET /surveys/1, it was returning an array with a single object in it, rather than returning the object itself.
I think this caused restangular to think that a collection was being accessed (note that I was having to call getList rather than get in order to get a properly restangularized object).
Say we have the following server resource:
api.example.com/event/1
Which returns some arbitrary resource, say:
{
id: 1,
details: {
type: 'webinar',
....
},
attendees: [
{
user_id: 1,
first_name: 'Bob'
...
},
...
]
}
It might be useful for a client to make a request to get just the event details of the event but not the list of attendees.
Is it better to provided two separate URLs for the resources and force two separate requests if a client wants both resources?
api.example.com/event/{event_id}
api.example.com/attendees/{event_id}
Or is it better to offer the same two endpoints, but optionally have the first one support a GET param to toggle the attendee listing on or off
api.example.com/event/{event_id}?listAttendees={true|false}
api.example.com/attendees/{event_id}
Where the listAttendees parameter will either have the representation return the attendee list or not.
Is it an common practice to allow GET params to change the representation returned from a specific URL?
I'd say the most correct way to do that in REST would be with different media-types, or media-type parameters, but since most people don't use custom media-types, I often use something I call the zoom protocol. The idea is that you have a zoom or expand parameter, with a numeric value, and it recursively includes the children entities, decreasing the parameter until it reaches zero.
So, a request like:
GET api.example.com/event/1
Returns the plain representation for the event resource, without embedding anything. A request like:
GET api.example.com/event/1?zoom=1
Would include the immediate children of event, in your case, the atendees. Following on that:
GET api.example.com/event/1?zoom=2
Would include the immediate children of event, the immediate children of atendees.
To answer your question, in REST the whole URI is an atomic identifier, so the parameters are part of the URI. That can be a problem if you're using something that won't interpret URIs in the same way, like old cache servers who won't cache URIs with a querystring.