I am designing a REST API and in this particular use case, trying to figure out the best way and what this hypermedia should look like.
The scenario is that the caller calls /persons?fields=lastName;filter=beginsWith=b because he wants back a list of people whos last names begin with "b".
Below shows the JSON response that I'm trying to figure out how best to mold/represent including hypermedia to associate with.
You can see a list of persons and you only see the name property because it's a partial representation of each person.
Then I try to add HATEAOS in here but not sure what would be the most useful to add and really what that should reference.
I figured ok, I can provide a href to the group (list) itself that I'm returning here. And if that's the case, where? I don't think it makes sense to put it in a root object like I'm doing. Because the expectation is to return a list of people, not also some root meta object so it doesn't feel good to me on what I have below.
OR
Does anyone think it isn't useful or not really embracing the HATEOS in this particular instance and I should instead provide some other type of href links in here?
JSON - List of person objects (representations) returned
[
"meta": {
"rel": "self",
"href": "http://ourdomain.api/persons?fields=lastName;filter=beginsWith=b"
},
{
"name": {
"last": "best"
}
},
{
"name": {
"last": "bettler"
}
},
{
"name": {
"last": "brown"
}
}
]
well, it is really your choice here, but I think that if you exclude a representation from navigability (i.e. not providing links to it) then it isn't HATEOAS anymore (just my opinion here).
In my opinion you could do something along the lines of the json you provided in the question, just separate clearly what is the result of the call and what is meta-information :
{
"meta": {
"rel": "self",
"href": "http://ourdomain.api/persons?fields=lastName;filter=beginsWith=b"
},
"resource" : [
{
"name": {
"last": "best"
}
},
{
"name": {
"last": "bettler"
}
},
{
"name": {
"last": "brown"
}
}
]
}
which is not so uncommon. Another approach would be to provide links to the list in every resource you return, with a relation like parent or source (maybe source makes more sense as it is a filtered query), e.g.
{[
{ "name" : {
"last" : "best"
},
"meta": {
"rel": "source",
"href": "http://ourdomain.api/persons?fields=lastName;filter=beginsWith=b"
}
},
{ ..and so on.. }
]}
after all, the list is just a container for the results, so it would feel more 'clean' (at least to me) not to have any hypermedia in it.
Related
I am trying to use a "LIKE" search on DynamoDB where I have an array of objects using nodejs.
Looking through the documentation and other related posts I have seen this can be done using the CONTAINS parameter.
My question is - Can I run a scan or query over all of my items in DynamoDB where a value in my object is LIKE "Test 2".
Here is my DynamoDB Table
This is how it looks as JSON:
{
"items": [
{
"description": "Test 1 Description",
"id": "86f550e3-3dee-4fea-84e9-30df174f27ea",
"image": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/86f550e3-3dee-4fea-84e9-30df174f27ea.jpg",
"live": 1,
"status": "new",
"title": "Test 1 Title"
},
{
"description": "Test 2 Description",
"id": "e17dbb45-63da-4567-941c-bb7e31476f6a",
"image": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/e17dbb45-63da-4567-941c-bb7e31476f6a.jpg",
"live": 1,
"status": "new",
"title": "Test 2 Title"
},
{
"description": "Test 3 Description",
"id": "14ad228f-0939-4ed4-aa7b-66ceef862301",
"image": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/14ad228f-0939-4ed4-aa7b-66ceef862301.jpg",
"live": 1,
"status": "new",
"title": "Test 3 Title"
}
],
"userId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
}
I am trying to perform a scan / query which will look over ALL users (every row) and look at ALL items and return ALL instances where description is LIKE "Test 2".
I have tried variations of scans as per the below:
{
"TableName": "my-table",
"ConsistentRead": false,
"ExpressionAttributeNames": {
"#items": "items",
},
"FilterExpression": "contains (#items, :itemVal)",
"ExpressionAttributeValues": {
":itemVal":
{
"M": {
"description": {
"S": "Test 2 Description"
},
"id": {
"S": "e17dbb45-63da-4567-941c-bb7e31476f6a"
},
"image": {
"S": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/e17dbb45-63da-4567-941c-bb7e31476f6a.jpg"
},
"live": {
"N": "1"
},
"status": {
"S": "new"
},
"title": {
"S": "Test 2 Title"
}
}
}
}
}
The above scan works but as you can see I am passing in the whole object as an ExpressionAttributeValues, what I want to do is just pass in the description for example something like the below (which doesnt work and returns no items found).
{
"TableName": "my-table",
"ConsistentRead": false,
"ExpressionAttributeNames": {
"#items": "items.description",
},
"FilterExpression": "contains (#items, :itemVal)",
"ExpressionAttributeValues": {
":itemVal":
{
"S": "Test 2"
}
}
}
Alternatively, would it be better to create a separate table where all the items are added and they are linked via the userId? I was always under the impression there should be one table per application but in this instance I think if I had all the item data at the top level, scanning it would be a lot safer and faster.
So with nearly 200 views since posting and no responses I have come up with a solution that does not immediately solve the initial problem (I honestly do not think it can be solved) but have come up with an alternative approach.
Firstly I do not want two tables as this seems overkill, and I do not want the aws costs associated with two tables.
This has lead me to restructure the primary keys with prefixes which I can search over using the "BEGINS_WITH" dynamodb selector query.
Users will be added as U_{USER_ID} and items will be added as I_{USER_ID}_{ITEM_ID}, this way I only have one table to manage and pay for and this allows me to run BEGINS_WITH "U_" to get a list of users or "I_" to get a list of items.
I will then flatten the item data as strings so I can run "contains" searches on any of the item data. This also allows me to run a "contains {USER_ID}" search on the primary keys for items so I can get a list of items for a particular user.
Hope this helps anyone who might come up against the same issue.
I have a fairly specific problem that I was hoping one of you really intelligent folk might know a solution for (or even a workaround at this stage)
Specifically, I'm dealing with action cables, nested JSON, and the JSONAPI.
I have an asset model, which has some attributes like name, desc etc. but it also has an attribute called state which is a complex nested JSON object.
// app/models/asset.js
export default DS.Model.extend({
// Attributes
name: DS.attr('string'),
desc: DS.attr('string'),
state: DS.attr(),
lastSeen: DS.attr('date'),
});
When anything on the asset changes in the backend, it is pushed down the cable to Ember, where it does a pushPayload(data), the payload looks like this;
{
"data": {
"id": "5",
"type": "assets",
"attributes": {
"asset_id": "962ABC",
"name": "962 ABC",
"desc": "Test Vehicle",
"activation_status": "active",
"state": {
"avl": {
"longitude": 152.9475426,
"reported_at": "2017-06-22T21:59:52Z"
},
"dfm": {
"in_alarm": false,
"reported_at": "2017-06-21T05:46:57Z",
"sensitivity": "normal",
"voice_prompt": false,
"driver_detected": true,
},
"tpms": {
"system_desc": "123ABC",
"system_type": "123_abc"
}
},
"last_seen": "2017-06-22T21:59:54.000Z"
},
"relationships": {
"company": {
"data": {
"id": "1",
"type": "companies"
}
},
"events": {
"links": {
"related": "/events/?asset_id=5"
}
},
"messages": {
"links": {
"related": "/messages/?asset_id=5"
}
}
}
}
}
This all works fine and dandy, updates to the asset & state are displayed as they happen thanks to the cable, and state is read only so I don't have to worry about saving anything. HOWEVER, I have noticed that when any single attribute on state changes in the backend, the entire asset is pushed down from the backend (this should be fine), and then this fires the observer for state and also all observers for state descendants - whereas I need it to only fire the observer for the state attribute that changed.
I have tried a number of things and each seemed to either not work at all, or still continue to update state in a way that fired off all state observers.
What I have tried;
ember-model-data-fragments (while it should work, I think the way that the action cable pushes the data must subvert this?)
embedded records (requires an ID for state, not currently compatible with JSONAPI)
raw json transform (making the json into ember objects, didn't seem to help)
Can anyone suggest a strategy or solution for me to try? I've spent nearly 2 days on this problem.. I would even settle for just splitting it up between avl/tpms/dfm, as long as when an attribute in one of those sections is changed, it doesn't notify properties from the other 2 sections.
Thanks
I'm working on a web service API using the HATEOAS REST representation.
My client can create an item (e.g. a stub of a blogpost):
POST /item
204 Created
Content-Type: application/vnd.foo.item+json
{
"id": 42,
"title": "Lorem Ipsum",
"status": "STUB",
"body": "Very long text."
"_links": {
"self": {
"href": "/item/42"
},
"activate": {
"href": "/item/42/activate"
},
}
}
After that the client can activate the item following the activate link (e.g. go live with the post). So it makes another call to the API:
POST /item/42/activate
200 Ok
Content-Type: application/vnd.foo.item+json
{
"id": 42,
"title": "Lorem Ipsum",
"status": "ACTIVE",
"body": "Very long text."
"_links": {
"self": {
"href": "/item/42"
},
"permalink": {
"href": "/item/42/permalink"
}
}
}
Up to here it is fine. But the problem is that I'm looking for a way to tell the client a suggestion about the next action to do (it's backend business logic).
In my case could be:
Bring the user to post page following the permalink
Bring the user to a shop cart to buy post extra features (visibility, more images, homepage positions and so on...)
Tell the user that the post is pending content review
I don't have an idea on how I could encapsulate this information in HATEOAS.
I was thinking to something like:
POST /item/42/activate
200 Ok
Content-Type: application/json
{
"suggested-action": "check-censure-panel",
"censure-reason": "censored (gambling)",
"_embedded": {
"foo.item": {
"id": 42,
"title": "Lorem Ipsum",
"status": "ACTIVE",
"body": "Very long text."
"_links": {
"self": {
"href": "/item/42"
},
"permalink": {
"href": "/item/42/permalink"
}
}
}
}
But the problem is that every suggested action is heterogeneous for extra attributes, another example may be:
"suggested-action": "go-to-checkout",
"product-order": 424242100,
They don't have a common interface, so I can't make a a vnd.foo.suggestedAction+json type.
What is the best way to design this response?
The next action is a state transition, and you seem to be using HAL so any state transitions should be presented as HAL.
Clients of your app need to react to what state transitions your app provides. So one very simple thing you could do is send a Location header to the next resource the app should present. You could even 302 redirect them there instead of 200'ing them with the updated resource.
You could provide the next action as a link...and not necessarily a HAL link. You could do it as a Link header (https://www.rfc-editor.org/rfc/rfc5988) but i think that would be weird, i just bring it up to knock home the point that your app needs to tell your client about a link.
You seem to want to use custom media types, but you could use profile links (https://www.rfc-editor.org/rfc/rfc6906) and mix in a profile into your vnd.foo type. You can stick to your vnd.foo type and just have it defined that there is an optional suggested-action link relationship. The problem in your example is you're defining it with data fields, but use a link:
{
"id": 42,
"title": "Lorem Ipsum",
"status": "ACTIVE",
"body": "Very long text."
"_links": {
"self": {
"href": "/item/42"
},
"permalink": {
"href": "/item/42/permalink"
},
"x:suggested-action" : {
"href" : "/path/to/best/action"
}
}
the client can follow that link, present the user with an option to follow that link, or ignore it. In the middle case, it's nice if your app provides some context to the user, like a title field:
"x:suggested-action" : {
"href" : "http://path/to/check/censure/panel",
"title" : "Check Censure Panel"
}
Also you can give a hint as to the resource the app can expect:
"x:suggested-action" : {
"href" : "http://path/to/check/censure/panel",
"title" : "Check Censure Panel",
"type" : "vnd.censure.panel/json"
}
I personally don't like doing that as i like my client to react to whatever i send them, but it's useful when you give multiple suggested actions:
"x:suggested-action" : [
{
"href" : "http://path/to/check/censure/panel",
"title" : "Check Censure Panel",
"type" : "vnd.censure.panel/json"
},
{
"href" : "http://path/to/checkout",
"title" : "Start Checkout",
"type" : "vnd.checkout/json"
}
]
now the app can decide based on well defined media types which of the suggested actions it wants to do, present, or ignore.
I am using template on elasticsearch 1.1.1 which is creating special mapping at index creation.
My template is as following :
{
"template": "*",
"mappings": {
"foo": {
"properties": {
"where": {
"type": "nested",
"properties": {
"*": {
"index": "not_analyzed"
},
"geo_point": {
"type": "geo_point"
}
}
}
}
}
}
}
There will be future fields in the nested object "where". I would like them to not be analyzed to avoid tokenization during a terms Faceting. Unfortunately I do not know the name of this future fields when my index is created.
Do you know any way of defining a mapping for any future field in a nested or inner object ?
Sincerely,
You can use dynamic templates for this, along with the index template that you already posted. Have a look here.
You can specify patterns that match your fields and have mappings apply to them instead of the usual defaults.
We are using Ember V1.0.0-pre.2 and our handlebars is as follows:
{{#each data.Product}}
<div>
{{Details.uid}} - {{Details.Name}}
</div>
{{/each}}
Our the 'data' bit is from this json:
{
"Product": [
{
"Details": {
"uid": "1",
"Name": "one"
}
},
{
"Details": {
"uid": "2",
"Name": "two"
}
},
{
"Details": {
"uid": "3",
"Name": "three"
}
},
{
"Details": {
"uid": "4",
"Name": "four"
}
},
{
"Details": {
"uid": "5",
"Name": "five"
}
}
]
}
This fails with the following warning:
WARNING: Watching an undefined global, Ember expects watched globals to be setup by the time the run loop is flushed, check for typos
When I change Details.whatever to details.whatever the warning disappears.
Is this by design or can we get around it somehow? The data is returned from the server in a fixed format and we wouldn't want to use another interim model if we can avoid it.
Ember has a naming policy where "instances/attributes" always start with a lowercase letter whilst "classes" always start with an uppercase letter. I think that's probably where you are running into some issues, if possible you should be de-serialising your JSON into attributes starting with lowercase letters.
Relevant part pulled from the guides (http://emberjs.com/guides/object-model/classes-and-instances/):
By convention, properties or variables that hold classes are capitalized, while instances are not. So, for example, the variable Person would contain a class, while person would contain an instance (usually of the Person class). You should stick to these naming conventions in your Ember applications.
Naming convention apply also to model data, but if you can't change what is coming from your API you can get around this by defining a map on a per property basis, e.g.
App.Adapter.map('App.Product', {
details: {key: 'Details'},
name: {key: 'Name'},
fooBar: {key: 'FOO_BaR'}
...
});
see here for more reference on how to map your json to your models: https://github.com/emberjs/data/blob/master/packages/ember-data/lib/system/mixins/mappable.js
hope it helps