HATEOAS without sending the HTTP links in the response - hateoas

Hi there REST HATEOAS experts !
Here is a typical JSON response from an HATEOAS service
{
links: {
'add': 'http://myhost/API/students',
'csv-export': 'http://myhost/API/students/export'
},
list: [
{id: '1', name: 'John Doe', links: {'see': 'http://myhost/API/students/1'},
{id: '2', name: 'Jane Doe', links: {'see': 'http://myhost/API/students/2'},
...
]
}
From this we can see that sending full HTTP(s) links along with the response is
very space consuming (yes, I know about gzip)
hard to read (imagine when there are 1000 elements in list)
not sufficient since you still don't know which method to use (nor which content you need to send)
So what I am doing is the following :
{
resType: 'studentsCollection',
rels: ['add','csv-export'],
list: [
{id: '1', name: 'John Doe', resType: 'studentCollectionItem', rels: ['see'],
{id: '2', name: 'Jane Doe', resType: 'studentCollectionItem', rels: ['see'],
...
]
}
And I provide additional endpoints :
/resTypes/studentsColelction/rels
{
resType: 'studentsCollection',
links: {
'add': {method: 'POST', url: '/students', contentType: 'studentForm', resultType: 'studentId'},
'csv-export': {method: 'GET', url: '/students/export', contentType: 'studentCriteria', resultType: 'binary'}
}
}
/resTypes/studentCollectionItem/rels
{
resType: 'studentCollectionItem',
links: {
'see': {method: 'GET', url: '/students/{id}', resultType: 'studentEntity'}
}
}
There also exists /resTypes which return all the resTypes at once to avoid multiple roundtrips

It sounds like JSON Hyper-Schema is a good fit for you. JSON Hyper-Schema is a hypermedia media type that you can use to apply links to your raw data without needing to modify your data.
Let's say you have this JSON response data
{
"list": [
{
"id": "1",
"name": "foo"
},
{
"id": "2",
"name": "bar"
}
]
}
That data can then be describedby the following Hyper-Schema.
{
"title": "Students",
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "string" },
"name": { "type": "string" }
},
"links": [
{ "rel": "student", "href": "/students/{id}" }
]
}
}
}
"links": [
{
"rel": "create", "href": "/students",
"method": "POST",
"schema": { "$ref": "/schemas/createStudent" }
}
]
}
The Hyper-Schema is like a hypermedia template that can be applied to a plain JSON document to transform a plain JSON response into a hypermedia enabled JSON response. Notice the URL template in the example /students/{id}. The {id} variable is filled in from the instance data. Therefore, the first item in the list has a link /students/1 and the second item has a link /students/2. This allows you to not have to duplicate all of that link boilerplate in every response.
You use a header Link to associate a JSON response with a Hyper-Schema
Link: </schema/students>; rel="describedby"
At this point you might be thinking, "that's nice but the extra request to get the Hyper-Schema for each response takes even more resources than sending bloated responses". That's why you should design your system so that Hyper-Schemas are cacheable forever. Then when the client sees a describedby link for a resource it already has, it just uses it's cached version. No additional request needed. If you ever need to change the Hyper-Schema, just change the describedby link to a different URI /schema/v2/student and the client will do a one time download of the new Hyper-Schema. Despite the occasional extra request to retrieve a Hyper-Schema, your overall bandwidth usage should be less than if you send all of your links in every request.
This example uses the draft-04 version JSON Hyper-Schema. Since draft-04, JSON Schema has been "under new management" so to speak. Although draft-04 is far from perfect, I'm personally not happy with the direction they have taken the spec since draft-04. However, I encourage you to checkout the latest draft (currently draft-07) as well and make your own decision.

Related

Generate code for different requests types from config

I have an app that makes a lot of requests to different urls.
Each of them have different request & response structure.
Configurations of the requests types located in Mysql databse.
It contains next data:
URL
Method
Query params
Body params
Headers
Response structure
So while before I was using Node.js for this things it was easy to make solution for this.
But with Go I see only way in using reflect package for this. But reflect harms performance of the app.
Is there a simple way to generate code for this?
Example of request config:
{
id: 1,
name: "Req1",
url: "http://example.com",
query: [
{name: "user_id", source: "user", value: "id" },
{name: "user_ip", source: "user", value: "ip" },
{name: "token", source: "const", value: "xxx" },
],
headers: [
{name: "Accept-Language", source: "user", value: "language"}
],
body: [
{"name": "user.ua", "user", "ua"}
]
}
User example:
{
ip: "127.0.0.1",
id: "123",
ua: "User Agent...",
"language": "en"
}
And in the output should be made next request:
URL: http://example.com?user_id=123&user_ip=127.0.0.1&token=xxx
Headers:
{
Accept-Language: en
}
Body:
{
user: {
ua: "User Agent..."
}
}
In body might be used path of the param.
Is there a tool for automatically generating this type of code?

Cannot save a very simple fhir patient bundle

I am trying to save this very simple fhir patient bundle against https://vonk.fire.ly/Bundle, by doing a PUT using Postman, however I am not able to get it working. When I simply copy the inner Patient resource data and do a PUT directly to the https://vonk.fire.ly/Patient endpoint it works just fine (for example - I just did it to this url https://vonk.fire.ly/Patient/deb7338181).
Can someone please please point me in the direction of what exactly it is going wrong here in this bundle??
{
"resourceType": "Bundle",
"id": "b6ec685a-26a2-4bb3-814b-841fba6a6edb",
"meta": {
"lastUpdated": "2018-05-29T23:45:32Z"
}
"type": "transaction",
"entry": [
{
"resource": {
"resourceType": "Patient",
"id": "deb73381811",
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">Some narrative</div>"
},
"active": true,
"name": [
{
"use": "official",
"family": "Chalmers1",
"given": [
"Peter1",
"James1"
]
}
],
"gender": "male",
"birthDate": "1974-12-25"
},
"request": {
"method": "POST",
"url": "Patient"
}
}
]
}
If you want to send a transaction to a FHIR server, you do a POST of the transaction Bundle to the endpoint, just like you mention in your comment. Within the transaction, for each entry you have to set the request part to the kind of request you want.
For your Patient entry you have asked the server to do a POST, which means a create with a server assigned ID. If you want the server to use your own ID, you should instruct it to perform a PUT, which is usually an update, but can also be used for create with your own ID.
The syntax for the update request is:
"request": {
"method": "PUT",
"url": "Patient/<my_patient_id>"
}
Please note that although it is a valid FHIR request and Vonk allows it, not all servers will.

Api blueprint failing with dredd on a realtime api?

I am in the middle of upgrading dredd from 1.08 to the latest version, while at it, I am trying to validate our api documentation, written in blueprint with the realtime test api and it is failing.
Since the tests are running against a real time api, the response returned from the api contains different values than specified in the blurprint document. I receive the following error from dredd.
Could someone help me figure it out ? :)
body: At '/data/email' No enum match for: "dredd_testzz#keyflow.se"
body: At '/data/firstName' No enum match for: "Sniper"
body: At '/data/lastName' No enum match for: "Wolf"
body: At '/data/verified' No enum match for: false
## `ResponseSchema` (object)
+ email: `john.doe#example.com` (string, required) - Email address
+ firstName: John (string, required) - First name
+ lastName: Doe (string, required) - Last name
+ verified: true (boolean, required) - True
# Group Account
## Login [/login/?]
Login user
### Login [POST]
Authentication required.
+ Request (application/json)
+ Attribute (LoginInputSchema)
+ Response 200 (application/json; charset=UTF-8)
+ Attribute
+ status: 200 (number, required, fixed)
+ data (ResponseSchema, required, fixed)
The JSON Schema generated by dredd is below
bodySchema: {
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"status": {
"type": "number",
"enum": [
200
]
},
"data": {
"type": "object",
"properties": {
"email": {
"type": "string",
"enum": [
"john.doe#example.com"
],
"description": "Email address of the guest."
},
"firstName": {
"type": "string",
"enum": [
"John"
],
"description": "First name of the guest."
},
"lastName": {
"type": "string",
"enum": [
"Doe"
],
"description": "Last name of the guest."
},
"verified": {
"type": "boolean",
"enum": [
true
],
"description": "The user is verified or not"
},
},
"required": [
"email",
"firstName",
"lastName",
"verified",
],
"additionalProperties": false
}
},
"required": [
"status",
"data"
]
}
TL;DR: Try to use fixed-type instead of fixed in your API Blueprint document. fixed requires the sample values to be the actual values.
More elaborate explanation:
body: At '/data/email' No enum match for: "dredd_testzz#keyflow.se"
This means the response returned by the server under test contains a correctly parseable JSON body, but the body isn't valid according to the schema provided by the API description.
The error points to /data/email, which means the {"data": {"email": ... property is problematic. Further, it mentions that enum of values is expected, and that the actual response contains dredd_testzz#keyflow.se, which isn't allowed by the enum. The other errors are similar.
Looking at the API description, the specification of what is expected in the response goes as follows:
+ Attribute
+ status: 200 (number, required, fixed)
+ data (ResponseSchema, required, fixed)
The fixed attribute, as explained in the 4.3 Nested Member Types section of the MSON spec, fixes not only the structure, but also all values, and propagates further down the data structure:
...MAY specify fixed to indicate a "value object" where all the properties MUST be present and the values of the properties MUST be the values specified, if any, in its Nested Member Types. Further, such an object type structure MUST NOT contain any other properties.
I think you want to use fixed-type instead, which fixes just the structure. This is further explained also in the Making Dredd Validation Stricter section of the Dredd docs.

AJAX call to REST service doesn't display results in page but a call to the same response in a flat file does

I'm trying to make a call to WCF Data Services and display the results in a GridPanel.
The call works and returns the correct JSON except the GridPanel doesn't display any results.
I tried copying the returned json into a file also on the webserver and replacing the destination url for that. This worked correctly and displays the results.
So as far as I can tell the JSON, the code and the service are all correct but they don't work together properly.
The Ext JS
Ext.define('Customer', {
extend: 'Ext.data.Model',
fields: ['Id', 'CustomerName'],
proxy: {
headers: {
'Accept' : 'application/json'
},
type: 'rest',
url: 'Service.svc/Customers',
reader: {
type: 'json',
root: 'd'
}
}
});
The JSON back from the service
{
"d" : [
{
"__metadata": {
"uri": "http://localhost:52332/testservice.svc/Customers(1)",
"type": "PierbridgeShinePlatformTestModel.Customer"
},
"Id": 1,
"CustomerName": "fred",
"Invoices": {
"__deferred": {
"uri": "http://localhost:52332/testservice.svc/Customers(1)/Invoices"
}
}
},
{
"__metadata": {
"uri": "http://localhost:52332/testservice.svc/Customers(2)",
"type": "PierbridgeShinePlatformTestModel.Customer"
},
"Id": 2,
"CustomerName": "Mr Fred",
"Invoices": {
"__deferred": {
"uri": "http://localhost:52332/testservice.svc/Customers(2)/Invoices"
}
}
}
]
}

Edit parsed JSON

I have a JSON file contact.txt that has been parsed into an object called JSONObj that is structured like this:
[
{
"firstName": "John",
"lastName": "Smith",
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021"
},
"phoneNumbers": [
{ "type": "home", "number": "212 555-1234" },
{ "type": "fax", "number": "646 555-4567" }
]
},
{
"firstName": "Mike",
"lastName": "Jackson",
"address": {
"streetAddress": "21 Barnes Street",
"city": "Abeokuta",
"state": "Ogun",
"postalCode": "10122"
},
"phoneNumbers": [
{ "type": "home", "number": "101 444-0123" },
{ "type": "fax", "number": "757 666-5678" }
]
}
]
I envision editing the file/object by taking in data from a form so as to add more contacts. How can I do this?
The following method for adding a new contact to the JSONObj's array doesn't seem to be working, what's the problem?:
var newContact = {
"firstName": "Jaseph",
"lastName": "Lamb",
"address": {
"streetAddress": "25 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "13021"
},
"phoneNumbers": [
{ "type": "home", "number": "312 545-1234" },
{ "type": "fax", "number": "626 554-4567" }
]
}
var z = contact.JSONObj.length;
contact.JSONObj.push(newContact);
It depends on what technology you're using. The basic process is to read the file in, convert it to whatever native datatypes (hash, dict, list, etc.) using a JSON parsing library, modify or add data to the native object, then convert it back to JSON and store it to the file.
In Python, using the simplejson library it would look like this:
import simplejson
jsonobj = simplejson.loads(open('contact.txt'))
#python's dict syntax looks almost like JSON
jsonobj.append({
'firstName': 'Steve',
'lastName': 'K.',
'address': {
'streetAddress': '123 Testing',
'city': 'Test',
'state': 'MI',
'postalCode': '12345'
},
'phoneNumbers': [
{ 'type': 'home', 'number': '248 555-1234' }
]
})
simplejson.dump(jsonobj, open('contact.txt', 'w'), indent=True)
The data in this example is hardcoded strings, but it could come from another file or a web application request / form data, etc. If you're doing this in a web app though I would advise against reading and writing to the same file (in case two requests come in at the same time).
Please provide more information if this doesn't answer your question.
In response to "isn't there way to do this using standard javascript?":
To parse a JSON string in Javascript you can either eval it (not safe) or use a JSON parser like this one's JSON.parse. Once you have the converted JSON object you can perform whatever modifications you want to it in standard JS. You can then use that same library to convert a JS object to a JSON string (JSON.stringify). Javascript does not allow file access (unless you're doing serverside JS), so that would prevent you from reading & writing to your contact.txt file directly. You'd have to use a serverside language (like Python, Java, etc.) to read and write the file.
Once you have read in the JSON, you just have an associative array - or rather you have a pseudo-associative array, since this is Javascript. Either way, you can treat the thing as one big list of dictionaries. You can access it by key and index.
So, to play with this object:
var firstPerson = JSONObj[0];
var secondPerson = JSONObj[1];
var name = firstPerson['firstName'] + ' ' + firstPerson['lastName'];
Since you will usually have more than two people, you probably just want to loop through each dictionary in your list and do something:
for(var person in jsonList) {
alert(person['address']);
}
If you want to edit the JSON and save it back to a file, then read it into memory, edit the list of dictionaries, and rewrite back to the file.
Your JSON library will have a function for turning JSON into a string, just as it turns a string into JSON.
p.s. I suggest you observe JavaScript conventions and use camelcase for your variable names, unless you have some other customs at your place of employment. http://javascript.crockford.com/code.html

Resources