Is a partial representation of document a valid "set of changes" as per HTTP PATCH RFC? - http-patch

Here is what RFC 5789 says:
The PATCH method requests that a set of changes described in the request entity be applied to the resource identified by the Request-URI. The set of changes is represented in a format called a "patch document" identified by a media type. If the Request-URI does not point to an existing resource, the server MAY create a new resource, depending on the patch document type (whether it can logically modify a null resource) and permissions, etc.
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.
Let's say I have { "login": "x", "enabled": true }, and I want to disable it.
According to post "Please. Don't Patch Like An Idiot.", the proper PATCH request would be
[{ "op": "replace", "path": "/enabled", "value": false }]
However, let's take this request:
{ "enabled": false }
It also 'contains a set of instructions describing how a resource currently residing on the origin server should be modified', the only difference is that JSON property is used instead of JSON object.
It seems less powerful, but array changes can have some other special syntax if required (e.g. {"a":{"add":[], "remove":[]}}), and server logic might not be able to handle anything more powerful anyway.
Is it an improper PATCH request as per RFC? And if so, why?
And, on the other hand, would a { "op": "disable" } be a correct PATCH request?

the only difference is that JSON property is used instead of JSON object.
It's actually a bit deeper than that. The reference to RFC 6902 is important. The first request has a Content-Type of application/json-patch+json, but the second is application/json
The important thing is that you use a 'diff media type,' one that's useful for this purpose. You don't have to use JSON-PATCH, (I'm a big fan of json-merge-patch), but you can't just use anything you want. What you're asking about in the second part is basically 'can I make my own media type' and the answer is 'yes,' just please document it and register it with the IANA.

Related

"code": "6140" Duplicate Document Number Error

I am using QuickBooks. Somehow I am getting some weird error on creating one invoice.
{
"Fault": {
"Error": [
{
"Message": "Duplicate Document Number Error",
"Detail": "Duplicate Document Number Error : You must specify a different number. This number has already been used. DocNumber=O0010714 is assigned to TxnType=Invoice with TxnId=45823",
"code": "6140",
"element": ""
}
],
"type": "ValidationFault"
},
"time": "2020-12-15T04:54:25.476-08:00"
}
Why it is happening as there is no doc in the QuickBooks which says that doc number is a unique entity.
Short version: DocNumber should be unique in most cases. In some cases you can add include=allowduplicatedocnum arg in URL to allow that. Be aware that DocNumber can be automatically generated.
Long version: taken from DocNumber field documentation for Invoice
Reference number for the transaction. If not explicitly provided at
create time, this field is populated based on the setting of
Preferences:CustomTxnNumber as follows:
If Preferences:CustomTxnNumber is true a custom value can be provided.
If no value is supplied, the resulting DocNumber is null.
If Preferences:CustomTxnNumber is false, resulting DocNumber is system
generated by incrementing the last number by 1.
If Preferences:CustomTxnNumber is false then do not send a value as it
can lead to unwanted duplicates. If a DocNumber value is sent for an
Update operation, then it just updates that particular invoice and
does not alter the internal system DocNumber.
Note: DocNumber is an
optional field for all locales except France. For France locale if
Preferences:CustomTxnNumber is enabled it will not be automatically
generated and is a required field. If a duplicate DocNumber needs to
be supplied, add the query parameter name/value pair,
include=allowduplicatedocnum to the URI.
P.S. Late response, but may be useful for somebody in future.
UPD:
Looks like library from npm node quickbooks doesn't support that natively. Because method createBill doesn't allow to provide any params to URI. Even method module.create have just hardcoded url variable without ability to provide nothing extra.
In this case, if you still want to use include=allowduplicatedocnum, you have the following options:
monkey-patch this library;
make fork and do updates;
ask authors of that library to implement that instead of you;
find a library that will support that;
send raw requests to QBO from your code.

Custom update actions in RESTful Services

My API requires many different types of updates that can be performed by different types of roles. A ticket can have it's data updated, a ticket can be approved (which includes some information), a ticket can be rejected, a ticket can be archived (state that makes a ticket unable to be updated), etc.
I've recently started working as a backend developer and I really do not know what is the most correct approach to this situation but I've two ideas in mind:
A single update endpoint (e.g. /api/tickets/:id) that accepts an action field with the type of update that wants to be done to that file;
Multiple custom action endpoints (e.g. /api/tickets/:id/validate, /api/tickets/:id/accept, etc.)
Which one of those is the best approach to the situation when it comes to the REST architecture? If both are incorrect when it comes to REST, what would be the most correct approach? I couldn't really find any post on SO that answered my question so I decided to create my own. Thank you!
REST stands for Representational State Transfer, which means that the client and the server affect each other’s state by exchanging representations of resources.
A client might GET a representation of a ticket like this:
GET /api/tickets/123 HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 123,
"state": "new",
"archived": false,
"comments": []
}
Now the client can PUT a new representation (replacing it wholesale) or PATCH specific parts of the existing one:
PATCH /api/tickets/123 HTTP/1.1
Content-Type: application/json-patch+json
[
{"op": "replace", "path": "/state", "value": "approved"},
{"op": "add", "path": "/comments", "value": {
"text": "Looks good to me!"
}}
]
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 123,
"state": "approved",
"archived": false,
"comments": [
{"author": "Thomas", "text": "Looks good to me!"}
]
}
Note how the update is expressed in terms of what is to be changed in the JSON representation, using the standard JSON Patch format. The client could use the same approach to update any resource represented in JSON. This contributes to a uniform interface, decoupling the client from the specific server.
Of course, the server need not support arbitrary updates:
PATCH /api/tickets/123 HTTP/1.1
Content-Type: application/json-patch+json
[
{"op": "replace", "path": "/id", "value": 456}
]
HTTP/1.1 422 Unprocessable Entity
Content-Type: text/plain
Cannot replace /id.
Still, this might require more complexity on the server than dedicated operations like “approve”, “reject”, etc. You might want to use a library to implement JSON Patch. If you find that you need many custom actions which are hard to express in terms of a representation, an RPC architecture might suit you better than REST.
REST is all about resources. And the state of the resources should be manipulated using representations (such as JSON, XML, you name it) on the top of stateless communication between client and server.
Once URIs are meant to identify the resources, it makes sense to use nouns instead of verbs in the URI. And when designing a REST API over the HTTP protocol, HTTP methods should be used to indicate the action intended to be performed over the resource.
Performing partial updates
You could use the PATCH HTTP verb to perform partial updates to your resource. The PATCH request payload must contain set of changes to applied to the resource.
There are a couple of formats that can be used to describe the set of changes to be applied to the resource:
###JSON Merge Patch
It's defined in the RFC 7396 and can be applied as described below:
If the provided merge patch contains members that do not appear within the target, those members are added. If the target does contain the member, the value is replaced. Null values in the merge patch are given special meaning to indicate the removal of existing values in the target.
So a request to modify the status of a ticket could be like:
PATCH /api/tickets/1 HTTP/1.1
Host: example.org
Content-Type: application/merge-patch+json
{
"status": "valid"
}
###JSON Patch
It's defined in the RFC 6902 and expresses a sequence of operations to be applies to a target JSON document. A request to modify the status of a ticket could be like:
PATCH /api/tickets/1 HTTP/1.1
Host: example.org
Content-Type: application/json-patch+json
[
{
"op": "replace",
"path": "/status",
"value": "valid"
}
]
The path is a JSON Pointer value, as described in the RFC 6901.
Try to either
Deal with a single object -> api/v1/tickets/1
Deal with a list of objects -> api/v1/tickets/.
Then try to capture all actions as CRUD actions.
Create object(s) -> HTTP POST
Retreive object(s) -> HTTP GET
Update object(s) -> HTTP PATCH
Delete object(s) -> HTTP DELETE
And also:
Save object(s) entirely -> HTTP PUT
When you are changing statuses, and these are just attributes on a ticket. I would send a PATCH request, for instance. If I need to change the statues of ticket #1 to "rejected" I would send something like PATCH api/v1/tickets/1 with a payload like:
{
"status": "rejected"
}
REST has a lot of best practices but not everything is set in stone. Maybe this tutorial: https://restfulapi.net can help you?
Really it all comes down to a matter of taste. It is often observed in the industry to have the static parameters in the URL (eg: /tickets/update, /users/add, /admin/accounts) and the variable parameters in the query (eg: IDs, messages, names). It allows to have a fixed number of endpoints.
I see you're using NodeJS so you're probably using Express and in Express getting the url parameters and the body parameters are equally easy:
// supposing you're using a JSON-based API with body-parser for JSON parsing
app.get('/some/path/:someParam', (req, res) => {
console.log(req.params.someParam);
console.log(req.body.someOtherParam);
res.send();
}

Check whether or not a query parameter is set

In Azure API Management, I need to check whether or not a query parameter is set. To achieve this, I'm trying to use context.Request.Url.Query.GetValueOrDefault(queryParameterName: string, defaultValue: string).
According to the documentation, this expression works as follows -
Returns comma separated query parameter values or defaultValue if the parameter is not found.
With that in mind, I used the example from the MS blog Policy Expressions in Azure API Management, to create the following <inbound> policy -
<set-variable name="uniqueId" value="#(context.Request.Url.Query.GetValueOrDefault("uniqueId", ""))" />
However, whenever I include this policy, execution fails with 404 Resource Not Found. Upon inspection of the trace, I can see that the execution was aborted without error before a single policy was evaluated (no matter where within <inbound> the above policy is placed.
This behavour results in the following <backend> trace, which explains the 404
"backend": [
{
"source": "configuration",
"timestamp": "2017-09-07T12:42:13.8974772Z",
"elapsed": "00:00:00.0003536",
"data": {
"message": "Unable to identify Api or Operation for this request. Responding to the caller with 404 Resource Not Found."
}
}
],
Given that the MS documentation seems to be inaccurate, how can I check whether or not a query parameter is set?
So the answer here is that there is (another) MS bug.
When the API operation was originally created, the uniqueId query parameter was set as required. I changed this so that it was not required before adding the policy described in my question, however a bug within the new Azure Portal means that when you uncheck the Required box adjacent to the query parameter and then save your changes, they are ignored.
I was able to work around this behaviour be editng the YAML template in the OpenAPPI specification view, removing the declaration required: true for the query parameter in question. The expresion within my policy now works as expected.
Please note: that this workaround sheds light on yet another bug, where saving the template results in your policies being deleted, so make sure you take a copy first.

CouchDB Read Configuration from design document

I would like to store a value in the config file and look it up in the design document for comparing against update values. I'm sure I have seen this but, for the life of me, I can't seem to remember how to do this.
UPDATE
I realize (after the first answer) that there was more than one way to interpret my question. Hopefully this example clears it up a little. Given a configuration:
curl -X PUT http://localhost:5984/_config/shared/token -d '"0123456789"'
I then want to be able to look it up in my design document
{
"_id": "_design/loadsecrets",
"validate_doc_update": {
"test": function (newDoc,oldDoc) {
if (newDoc.supersecret != magicobject.config.shared.token){
throw({unauthorized:"You don't know the super secret"});
}
}
}
}
It's the abilitly to do something like the magicobject.config.shared.token that I am looking for.
UPDATE 2
Another potentially useful (contrived) scenario
curl -X PUT http://trustedemployee:5984/_config/eventlogger/detaillevel -d '"0"'
curl -X PUT http://employee:5984/_config/eventlogger/detaillevel -d '"2"'
curl -X PUT http://vicepresident:5984/_config/eventlogger/detaillevel -d '"10"'
Then on devices tracking employee behaviour:
{
"_id": "_design/logger",
"updates": {
"logger": function (doc,req) {
if (!doc) {
doc = {_id:req.id};
}
if(req.level < magicobject.config.eventlogger.detaillevel ){
doc.details = req.details;
}
return [doc, req.details];
}
}
}
Here's a follow-up to my last answer with more general info:
There is no general way to use configuration, because CouchDB is designed with scalability, stability and predictability in mind. It has been designed using many principles of functional programming and pure functions, avoiding side effects as much as possible. This is a Good Thing™.
However, each type of function has additional parameters that you can use, depending on the context the function is called with:
show, list, update and filter functions are executed for each request, so they get the request object. Here you have the req.secObj and req.userCtx to (ab)use for common configuration. Also, AFAIK the this keyword is set to the current design document, so you can use the design doc to get common configuration (at least up to CouchDB 1.6 it worked).
view functions (map, reduce) don't have additional parameters, because the results of a view are written to disk and reused in subsequent calls. map functions must be pure (so don't use e.g. Math.random()). For shared configuration across view functions within a single design doc you can use CommonJS require(), but only within the views.lib key.
validate doc update functions are not necessarily executed within a user-triggered http request (they are called before each write, which might not be triggered only via http). So they have the userCtx and secObj added as separate parameters in their function signature.
So to sum up, you can use the following places for configuration:
userCtx for user-specific config. Use a special role (e.g. with a prefix) for storing small config bits. For example superLogin does this.
secObj for database-wide config. Use a special member name for small bits (as you should normally use roles instead of explicit user names, secObj.members.names or secObj.admins.names is a good place).
the design doc itself for design-doc-wide config. Best use the this.views.lib.config for this, as you can also read this key from within views. But keep in mind that all views are invalidated as soon as you change this key. So if the view results will stay the same no matter what the config values are, it might be better to use a this.config key.
Hope this helps! I can also add examples if you wish.
I think I know what you're talking about, and if I'm right then what you are asking for is no longer possible. (at least in v1.6 and v2.0, I'm not sure when this feature was removed)
There was a lesser-known trick that allowed a view/show/list/validation/etc function to access the parent design document as this in your function. For example:
{
"_id": "_design/hello-world",
"config": {
"PI": 3.14
},
"views": {
"test": {
"map": "function (doc) { emit(this.config.PI); })"
}
}
}
This was a really crazy idea, and I imagine it was removed because it created a circular dependency between the design document and the code of the view that made the process of invalidating/rebuilding a view index a very tricky affair.
I remember using this trick at some point in the distant past, but the feature is definitely gone now. (and likely to never return)
For your special use-case (validating a document with a secret token), there might be a workaround, but I'm not sure if the token might leak in some place. It all depends what your security requirements are.
You could abuse the 4th parameter to validate_doc_update, the securityObject (see the CouchDB docs) to store the secret token as the first admin name:
{
"test": "function (newDoc, oldDoc, userCtx, secObj) {
var token = secObj.admins.names[0];
if (newDoc.supersecret != token) {
throw({unauthorized:"You don't know the super secret"});
}
}"
}
So if you set the db's security object to {admins: {names: ["s3cr3t-t0k3n"], roles: ["_admin"]}}, you have to pass 's3cr3t-t0k3n' as the doc's supersecret property.
This is obviously a dirty hack, but as far as I remember, the security object may only be read or modified by admins, you wouldn't immediately leak your token to the public. But consider adding a separate layer between the CouchDB and your caller if you need "real" security.

Etag and LastModifiedDate donot change after file editing

After editing a document in GoogleDrive, it seems that the LastModified value doesn't change immediately sometimes. It looks like the value will be updated in a few minutes.
I tried to fetch the Etag of the document by DriveSDK, it also happens to the Etag value.
This strange behavior stops me getting the document status (modified or not) at real time. Any suggestions will be highly appreciated.
[Sample Request and Response]
The request is just GoogleDrive ListFiles : https://www.googleapis.com/drive/v2/files
Here's the part of the response JSON, and you can see that the ModifiedDate is earlier than ModifiedByMeDate.
{
"kind": "drive#fileList",
"etag": "\"3NNCnvnQuji-pODa6SMQ6atlc3M/oKnf21kAcJKTCIycS597xCSR2bk\"",
"selfLink": "https://www.googleapis.com/drive/v2/files",
"items": [
{
"kind": "drive#file",
"id": "1lZjcJIf3Chuu5upFqtiqfTRnRw7*****rFL_tlO8A",
"etag": "\"3NNCnvnQuji-pODa6SMQ6atlc3M/MTM1NDY5MzMyMzQ1Mg\"",
"selfLink": "https://www.googleapis.com/drive/v2/files/1lZjcJIf3Chuu5upFqtiqfTRnRw7wSUjbRurFL_tlO8A",
"alternateLink": "https://docs.google.com/a/*****.com/document/d/1lZjcJIf3Chuu5upFqtiqfTRnRw7wSUjbRurFL_tlO8A/edit",
"embedLink": "https://docs.google.com/a/*****.com/document/d/1lZjcJIf3Chuu5upFqtiqfTRnRw7wSUjbRurFL_tlO8A/preview",
"iconLink": "https://ssl.gstatic.com/docs/doclist/images/icon_11_document_list.png",
"thumbnailLink": "https://docs.google.com/feeds/vt?gd=true&id=1lZjcJIf3Chuu5upFqtiq*****7wSUjbRurFL_tlO8A&v=22&s=AMedNnoAAAAAU*****UK74n3UiIg0L4TY-NwP3EaAU&sz=s220",
"title": "TestFile",
"mimeType": "application/vnd.google-apps.document",
"labels": {
"starred": false,
"hidden": false,
"trashed": false,
"restricted": false,
"viewed": true
},
"createdDate": "2012-07-24T08:14:13.918Z",
"modifiedDate": "2012-12-06T01:49:57.982Z",
"modifiedByMeDate": "2012-12-06T01:49:57.982Z",
"lastViewedByMeDate": "2012-12-06T01:50:06.974Z",
"parents": [
{
"kind": "drive#parentReference",
"id": "0AJ-aGTt-gWksUk9PVA",
"selfLink": "https://www.googleapis.com/drive/v2/files/1lZjcJIf3Chuu5upFqtiqfTRnRw7wSUjbRurFL_tlO8A/parents/0AJ-aGTt-gWksUk9PVA",
"parentLink": "https://www.googleapis.com/drive/v2/files/0AJ-aGTt-gWksUk9PVA",
"isRoot": true
}
],
"exportLinks": {
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": "https://docs.google.com/feeds/download/documents/export/Export?id=1l*****huu5upFqtiqfTRnRw7wSUjbRurFL_tlO8A&exportFormat=docx",
"application/vnd.oasis.opendocument.text": "https://docs.google.com/feeds/download/documents/export/Export?id=1lZjcJIf3Chuu5upF*****SUjbRurFL_tlO8A&exportFormat=odt",
"text/html": "https://docs.google.com/feeds/download/documents/export/Export?id=1lZjcJIf3Chuu5upFqtiqfTRnRw7wSUj*****L_tlO8A&exportFormat=html",
"application/rtf": "https://docs.google.com/feeds/download/documents/export/Export?id=1lZjcJIf3Chuu5upFqtiq*****SUjbRurFL_tlO8A&exportFormat=rtf",
"text/plain": "https://docs.google.com/feeds/download/documents/export/Export?id=1lZjcJIf3Chuu5upFqtiqf*****SUjbRurFL_tlO8A&exportFormat=txt",
"application/pdf": "https://docs.google.com/feeds/download/documents/export/Export?id=1lZjcJIf3C*****fTRnRw7wSUjbRurFL_tlO8A&exportFormat=pdf"
},
"userPermission": {
"kind": "drive#permission",
"etag": "\"3NNCnvnQuji-pODa6SMQ6atlc3M/6LfqbkrWujmUe1WSDkyhTxdIUCc\"",
"id": "me",
"selfLink": "https://www.googleapis.com/drive/v2/files/1lZjcJIf3Chuu5upFqtiqfTRnRw7wSUjbRurFL_tlO8A/permissions/me",
"role": "owner",
"type": "user"
},
"quotaBytesUsed": "0",
"ownerNames": [
"***** *****"
],
"lastModifyingUserName": "***** *****",
"editable": true,
"writersCanShare": true,
"appDataContents": false
},
This doesn't answer the question but I would just like to comment on what etag and modifiedDate means on File resources. File etag seems to just directly correspond to the modifiedDate value. The second part of the etag (after the slash) is just an encoding of the modifiedDate.
In the Documents List API, there's a property called app:edited besides the updated property (which seems to correspond to modifiedDate here). The way these timestamps change are not clearly documented, but I've noticed that app:edited changes on virtually every change (e.g. starring of a file, even by another user if I remember correctly) and updated changes are more limited (e.g. for content modifications and permission changes). And the etag before directly corresponds to app:edited, which is good because a change of app:edited indicates that there was really a change (sometimes it changes too much though, like starring of a file by a different user, as it doesn't really affect the current user's metadata).
What I want to say is that I think Documents List's etag (and having the app:edited property) is better than the Drive API having modifiedDate only. The problem with the latter is that you don't have a reliable way to determine if a File resource has changed or not. For example, if you just like to check if a File resource has changed, you could've used the files.get method with an If-None-Match header using the etag. But etag doesn't always change because modifiedDate doesn't always change. It changes, for example, when there's new content, or changed ACL, or changed description, but does not change, for example, when trashed, or parents changed. When comparing two resources, you can't reliably determine which one is newer. Add the fact that modifiedDate can be set (e.g. it would be possible to set the modifiedDate to an earlier value, or even a constant value, and the etag with it). The intended purpose of etag is lost. There are many cases where we can't just rely on the changes list, and if etag behaves properly, it would be huge help for determining the changes that happened.
What I suggest is to restore the behavior of etag to be similar to the Documents List API. And to help in determining which resource has newer information, add back the app:edited property as well (I think just documenting the value of etag is enough, like officially saying that the second part is an encoding of a timestamp, so that we can rely on it to be always increasing). Also, I think it's better to just change the modifiedDate property on content modification, and nothing else (e.g. not with ACL changes or description changes).
Similarly, etag of Revision(List) resources of verbatim files is not very useful as well. Since the downloadUrl property changes regularly, the etags of each Revision and the whole list changes with it, rendering it useless (because you can't use it to check if the revisions didn't change). Good thing there is the md5Checksum property you can compare, but it's not reliable in some cases.
Google's infrastructure updates Google docs asynchronously after updates. To my knowledge, this affects etag, thumbnails, and probably md5sum and certain modified dates.
Some documentation from Google confirming which items are asynchronous and which are synchronous would be tremendously useful to developers.
The etag is in effect, and often in practice, a hash of the file's metadata. If you change anything about a regular file (not a Doc or Sheet, see below), or just "touch" it using eg
https://www.googleapis.com/drive/v2/files/blah_my_file_id/touch
the metadata is changed, and the etag you get back in the response WILL be different. My application, and likely thousands of others, utterly depend on this behavior.
If you're seeing different, it's somewhere in your setup. Where the OP said "you can see that the ModifiedDate is earlier than ModifiedByMeDate"... er, it's not. It's identical in the JSON response they provided. The "LastViewedByMeDate" is different.
Google Docs, Sheets etc, are a completely different system since they are collaborative. Collaborative systems keep individual changes, and need very complex logic to keep it all straight; they're not just regular files. What's "in" your Drive is just a shortcut to processes running somewhere else in Google's caves, and the metadata you read from Drive may well be out of sync with recent changes. Google's now withdrawn collaborative Realtime API (which may still drive Docs and Sheets, I don't know) provided a serverRevision property which was the reliable way to track if any changes had occurred.

Resources