I have a page where I list the books of a school. The user can update a book, add a new book or delete an existing book. All actions must be saved when the form is submitted.
How can i map a rest API for that? I could take advantage of the endpoints i already have.
UPDATE
PUT /schools/1/books
{
"books": [
{
"id": "1",
"name": "Book 1"
}
]
}
CREATE
POST /schools/1/books
{
"books": [
{
"name": "Book 2"
},
{
"name": "Book 3"
}
]
}
DELETE
DELETE /schools/1/books
{
"books": [
{
"id": 2
}
]
}
But I need everything to run on the same transaction, and wouldn't make sense to submit 3 requests.
I also thought of creating a new endpoint where I would create books that doesn't exists, update books that exists, and remove books that are not present on the request.
So if this school has Book 1 and Book 2, I could update Book 1, create New Book and remove Book 2 with:
PUT /schools/1/batch-books
{
"books": [
{
"id": "1",
"name": "Updated Book 1"
},
{
"name": "New Book"
}
]
}
Do you guys have other options?
I would separate things into different resources:
/books and /books/{id} for books. They gives book details and allow to manage them.
/schools and /schools/{id} for schools. They gives school details and allow to manage them.
/schools/{id}/books to associate books in schools. I mean books that are available within a school. This resource provides methods to manage a list of links to books.
Let me detail the last resource. In fact, this is related to hypermedia. In the following, I'll use JSON-LD but you're free to use other hypermedia tools.
A GET method will return the list of associated books:
GET /schools/1/books
[
{
"#id": "http://api.example.com/books/1895638109"
},
{
"#id": "http://api.example.com/books/8371023509"
}
]
You can notice that you can implement mechanisms to allow to get more details if needed. Leveraging the Prefer header seems to be a great approach (see the link below for more details).
In addition, you could provide the following methods:
POST to add a link to the school. The request payload would be: {"#id": "http://api.example.com/books/1895638109"}. The response should be a 201 status code.
DELETE to delete a specific link from a school. A query parameter could be used to specify which link to remove.
PATCH to allow to do several operations in one call and actually provide some batch processing. You can leverage at this level JSON-PATCH for the request processing. Within the response, you could describe what happens. There is no specification at this level so you're free to use what you want... Here is a sample for the request payload:
PATCH /schools/1/books/
[
{
"op": "add", "value": "http://api.example.com/books/1895638109"
},
{
"op": "remove", "path": "http://api.example.com/books/8371023509"
}
]
Reading the following links could give you some hints on the way to design such use case:
Implementing bulk updates within RESTful services: http://restlet.com/blog/2015/05/18/implementing-bulk-updates-within-restful-services/
On choosing a hypermedia type: http://sookocheff.com/post/api/on-choosing-a-hypermedia-format/
Creating Client-Optimized Resource Representations in APIs: http://www.freshblurbs.com/blog/2015/06/25/api-representations-prefer.html
Hope it helps you,
Thierry
Related
It is possible to read data from a sheet only with API key (without OAuth 2.0), but it seems that reading the developer metadata requires OAuth 2.0.
Is there some way to read the metadata from an app without asking the user to connect his google account?
You want to retrieve the developer metadata of the Spreadsheet using the API key.
You have already been able to get values from Spreadsheet using the API key.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Issue and workaround:
Unfortunately, "REST Resource: spreadsheets.developerMetadata" in Sheets API cannot be used with the API key. In this case, OAuth2 is required as mentioned in your question. The developer metadata can be also retrieved by the method of spreadsheets.get in Sheets API. The developer metadata can be retrieved by the API key. And in this method, all developer metadata is retrieved. So when you want to search the developer metadata, please search it from the retrieved all developer metadata.
IMPORTANT POINTS:
In this case, please set the visibility of developer metadata to DOCUMENT. By this, the developer metadata can be retrieved by the API key. If the visibility is PROJECT, it cannot be retrieved with the API key. Please be careful this.
When you want to retrieve the developer metadata with the API key, please publicly share the Spreadsheet. By this, it can be retrieved with the API key. Please be careful this.
Sample situation 1:
As a sample situation, it supposes that it creates new Spreadsheet, and create new developer metadata to the Spreadsheet as the key of "sampleKey" and value of "sampleValue".
In this case, the sample request body of spreadsheets.batchUpdate is as follows.
{
"requests": [
{
"createDeveloperMetadata": {
"developerMetadata": {
"location": {
"spreadsheet": true
},
"metadataKey": "sampleKey",
"metadataValue": "sampleValue",
"visibility": "DOCUMENT"
}
}
}
]
}
Sample curl command:
When you retrieve the developer metadata from above sample Spreadsheet, please use the following curl command.
curl "https://sheets.googleapis.com/v4/spreadsheets/### spreadsheetId ###?key=### your API key ###&fields=developerMetadata"
In this case, fields=developerMetadata is used to make it easier to see the response value. Of course, you can also use * as fields.
In this case, when above endpoint is put to the browser, you can see the retrieved value, because of GET method.
Result:
{
"developerMetadata": [
{
"metadataId": 123456789,
"metadataKey": "sampleKey",
"metadataValue": "sampleValue",
"location": {
"locationType": "SPREADSHEET",
"spreadsheet": true
},
"visibility": "DOCUMENT"
}
]
}
Sample situation 2:
As other situation, it supposes that it creates new Spreadsheet, and create new developer metadata to the 1st column (column "A") as the key of "sampleKey" and value of "sampleValue".
In this case, the sample request body is as follows.
{
"requests": [
{
"createDeveloperMetadata": {
"developerMetadata": {
"location": {
"dimensionRange": {
"sheetId": 0,
"startIndex": 0,
"endIndex": 1,
"dimension": "COLUMNS"
}
},
"metadataKey": "sampleKey",
"metadataValue": "sampleValue",
"visibility": "DOCUMENT"
}
}
}
]
}
Sample curl command:
When you retrieve the developer metadata from above sample Spreadsheet, please use the following curl command.
curl "https://sheets.googleapis.com/v4/spreadsheets/### spreadsheetId ###?key=### your API key ###&fields=sheets(data(columnMetadata(developerMetadata)))"
In this case, sheets(data(columnMetadata(developerMetadata))) is used to make it easier to see the response value. Of course, you can also use * as fields.
Result:
{
"sheets": [
{
"data": [
{
"columnMetadata": [
{
"developerMetadata": [
{
"metadataId": 123456789,
"metadataKey": "sampleKey",
"metadataValue": "sampleValue",
"location": {
"locationType": "COLUMN",
"dimensionRange": {
"dimension": "COLUMNS",
"startIndex": 0,
"endIndex": 1
}
},
"visibility": "DOCUMENT"
}
]
},
{},
,
,
]
}
]
}
]
}
References:
Method: spreadsheets.developerMetadata.get
DeveloperMetadataVisibility
If I misunderstood your question and this was not the direction you want, I apologize.
I am new to Dialogflow so my question may be too simple. However, I do not understand what is the purpose of naming actions in Dialogflow. I have watched videos on youtube and people in them are using actions when they have a webhook. For example they may have an if condition in their source code
(e.g. in python
if action == 'action_name':
...
)
which executes something particular in this case.
However the json output which is retrieved by the source code has the following form:
{
"id": "123d9e8e-314f-451b-8b15-5e3b55baa980",
"timestamp": "2018-03-16T17:03:05.987Z",
"lang": "en",
"result": {
"source": "agent",
"resolvedQuery": "Hello",
"action": "input.welcome",
"actionIncomplete": false,
"parameters": {},
"contexts": [],
"metadata": {
"intentId": "effe6b2b-3372-4f89-882f-ff937b2b2abb",
"webhookUsed": "false",
"webhookForSlotFillingUsed": "false",
"intentName": "Welcome"
},
"fulfillment": {
"speech": "Hello, how can I help you?",
"messages": [
{
"type": 0,
"speech": "Hello, how can I help you?"
}
]
},
"score": 1
},
"status": {
"code": 200,
"errorType": "success",
"webhookTimedOut": false
},
"sessionId": "491d57cb-0af2-45ac-a658-9e47ec6658ce",
"alternativeResultsFromKnowledgeService": {}
}
Since the json data contains the IntentName why to bother naming an unique action for this specific intent when you can get directly the name of the intent in your json?
I tend to think of this in two ways, depending on exactly what I'm building. (Or sometimes a combination of these two ways.)
The Intent Name is a human-usable name, while the Action is something that is more intended for use by the webhook and more directly maps to a function.
Since you can have more than one Intent use the same Action, it can be convenient to map a few different ways the user may say something (and the parameters they may send along with them) to the same method. While you could do that by listing all the different Intent names in your code, it is easier to do that on the Dialogflow side.
In truth - use whatever works best for you. I tend to name my Intents and my Actions very similarly, but do branching based on what makes the most sense for the code (which sometimes also includes other values that may be sent).
What is the current best practice on using pragmatic hateoas when a POST object may differ from the GET object of the same resource? For example, would the following be bad API design?
For the resource /families a client may POST a new family and include many "members":[] in the same request.
/members is also a resource
Given a successful POST or GET, the server returns an object that includes a different "family" object to include links:
{
"id": 123,
"name": "The Adams Family",
"_links": {
"members": { "href": "/families/123/members" }
}
I think it would be better to use nested resources in this case e.g. in HAL+JSON:
{
"id": 123,
"name": "The Adams Family",
"_embedded": {
"members": {
"_links": {
"self": {
"href": "/families/123/members"
}
}
}
}
}
I think your solution is okay as well, but you have to define the members link relation in the documentation, so the clients (and their developers) will know that "members" means family members, and not for example members of any collection (e.g. hydra defines a member property for collection items and schema.org defines a members property for organization members).
I took a look at this question that seeks to address the issue of REST media-type explosion. One of the suggestions was to have a media-type that describes a collection of anything. So for example, we could have an application/vnd.collection+json which is a collection with well-defined semantics that can hold a list of references to other resources:
{
"size": "3"
"elements": [
{ "href" : "http://my.api.com/resource/1" },
{ "href" : "http://my.api.com/resource/2" },
{ "href" : "http://my.api.com/resource/3" }
]
}
I know an option to alleviate chattiness is to include embedded representations of resources. How would a "generic" media-type for lists accomplish that? Don't the semantics of the list change based on which embedded resource is inside it? This is especially relevant if embedded resources have different processing-rules (which would ideally be conveyed by the media type). Would be alright in this case to allow in-band information that describes the media type of the embedded resource? For example we could have application/vnd.collection+json for both links and embedded resources that do something like this:
{
"size": "3"
"element-content-type": "application/vnd.link+json"
"elements": [
{ "href" : "http://my.api.com/resource/1" },
{ "href" : "http://my.api.com/resource/2" },
{ "href" : "http://my.api.com/resource/3" }
]
}
and if it contains an embedded resource:
{
"size": "3"
"element-content-type": "application/vnd.resource+json"
"elements": [
{
"id": "1"
"name": "Cool Resource"
},
{
"id": "2"
"name": "Awesome Resource"
},
{
"id": "3"
"name": "Super Awesome Resource"
}
]
}
The assumption is that application/vnd.link+json and application/vnd.resource+json have been documented as well.
I thought about this a little bit more, and I think it is actually OK to include the content-type like that. The reason is, we already do this. In HTML the script tag has a type attribute that can be application/javascript or application/vbscript (for example). The provides the browser a hint as to how to process the content of that tag. Similarly, I think the content-type in the above example achieves the same purpose: it tells the client how to process the elements in the collection.
I wanted to update this answer some more. It appears that another way to do this is to use a rel. At least, this is how HAL does it. You can create a namespaced rel with a curie so that you end up resolving the rel to a URL that points to documentation about that resource. This way you have access to the documentation and that should tell you all you need to know about the resource(s).
I'm building a RESTful API that exposes my application's users as users.
My application also features 'documents' and each user has access to specific documents. I'm thinking the natural way to represent that is by exposing the accessible documents through users/{user-id}/documents.
However, from a usability perspective, it's important for my clients to be able to fetch (and modify) the users that have access to a specific document. Because of that I'm considering 'reversing' this representation to documents/{document-id}/users.
Do these (and especially the latter) seem like proper ways to model this relationship? If I do go with such a solution, how do I model 'granting access to a document'?
I'm leaning towards PUTing a pre-existing user (presumably acquired by GETing users) into documents/{document-id}/users/{user-id}. That seems unsatisfactory however, as I'll be doing an 'update' operation not to actually update the resource but to insert it into a collection. It is especially problematic in terms of semantics as I expect my server-side to ultimately not take into account the complete, sent user representation but rather only cross-reference the id with ids of pre-existing users in order to create an association.
On the other hand, I can't POST into documents/{document-id}/users as I'm not aiming at the creation of a new resource - I specifically don't want one to be created.
Am I doing it wrong?
The users don't really belong to the document resource, right? What you're really saying is these users have access to this document. So what should probably be returned from /documents/{document-id}/users is not a direct representation of the user entity, but instead some kind of representation of the user's permission to the entity. Perhaps inside of that representation is a link to the full user itself.
So, if you were returning the Collection+JSON media type, maybe you'd have something like:
{
"collection":
{
"version":"1.0",
"href":"/documents/document123",
"items":
[
{
"href":"/documents/document123/users/user3841",
"data": [
{ "name":"userName", "value":"John Doe", "prompt":"User Name" },
{ "name":"permissions", "value":["Read"], "prompt":"User Permissions" }
],
"links": [
{ "rel":"user", "href":"/users/3841" }
]
},
{
"href":"http//whatever/documents/document123/users/user9387",
"data": [
{ "name":"userName", "value":"John Doe", "prompt":"User Name" },
{ "name":"permissions", "value":["Read"], "prompt":"User Permissions" }
],
"links": [
{ "rel":"user", "href":"/users/9387" }
]
}
]
}
}