MXS-1220: Add support for PATCH
The PATCH method should now be used instead the PUT method to update resources. As PUT request bodies should represent complete resources, the use of PUT to update resources is no longer supported. Altered tests to use PATCH instead of PUT for updating resources.
This commit is contained in:
@ -111,10 +111,16 @@ Credentials for authentication.
|
|||||||
#### Content-Type
|
#### Content-Type
|
||||||
|
|
||||||
All PUT and POST requests must use the `Content-Type: application/json` media
|
All PUT and POST requests must use the `Content-Type: application/json` media
|
||||||
type and the request body must be a valid JSON representation of a resource. All
|
type and the request body must be a complete and valid JSON representation of a
|
||||||
PATCH requests must use the `Content-Type: application/json` media type and the
|
resource. All PATCH requests must use the `Content-Type: application/json` media
|
||||||
request body must be a JSON document containing a partial definition of the
|
type and the request body must be a JSON document containing a partial
|
||||||
original resource.
|
definition of the original resource.
|
||||||
|
|
||||||
|
The current version of the API supports PATCH-like PUT requests with
|
||||||
|
partial definitions of resources in the request body. This is discouraged
|
||||||
|
as it goes against the intended use of the PUT method. Future versions of
|
||||||
|
the MaxScale REST API can remove this support which means that this
|
||||||
|
functionality is deprecated.
|
||||||
|
|
||||||
#### Host
|
#### Host
|
||||||
|
|
||||||
|
@ -267,7 +267,7 @@ The :name in the URI must map to a monitor name with all whitespace replaced wit
|
|||||||
hyphens. The request body must be a valid JSON document representing the modified monitor.
|
hyphens. The request body must be a valid JSON document representing the modified monitor.
|
||||||
|
|
||||||
```
|
```
|
||||||
PUT /v1/monitor/:name
|
PATCH /v1/monitor/:name
|
||||||
```
|
```
|
||||||
|
|
||||||
### Modifiable Fields
|
### Modifiable Fields
|
||||||
|
@ -312,7 +312,7 @@ Status: 403 Forbidden
|
|||||||
### Update a server
|
### Update a server
|
||||||
|
|
||||||
```
|
```
|
||||||
PUT /v1/servers/:name
|
PATCH /v1/servers/:name
|
||||||
```
|
```
|
||||||
|
|
||||||
The _:name_ in the URI must map to a server name with all whitespace replaced
|
The _:name_ in the URI must map to a server name with all whitespace replaced
|
||||||
@ -443,12 +443,12 @@ Request for `PUT /v1/server/server1`:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The current implementation accepts both PUT and PATCH requests with partially
|
The current implementation accepts PATCH requests with partially defined
|
||||||
defined resources as request body. If parts of the resource are not defined
|
resources as request body. If parts of the resource are not defined (e.g. the
|
||||||
(e.g. the `attributes` field in the above example), those parts of the resource
|
`attributes` field in the above example), those parts of the resource are not
|
||||||
are not modified. All parts that are defined are interpreted as the new
|
modified. All parts that are defined are interpreted as the new definition of
|
||||||
definition of those part of the resource. In the above example, the
|
those part of the resource. In the above example, the `relationships` of the
|
||||||
`relationships` of the resource are completely redefined.
|
resource are completely redefined.
|
||||||
|
|
||||||
#### Response
|
#### Response
|
||||||
|
|
||||||
|
@ -265,7 +265,7 @@ The _:name_ in the URI must map to a service name and the request body must be a
|
|||||||
valid JSON Patch document which is applied to the resource.
|
valid JSON Patch document which is applied to the resource.
|
||||||
|
|
||||||
```
|
```
|
||||||
PUT /v1/services/:name
|
PATCH /v1/services/:name
|
||||||
```
|
```
|
||||||
|
|
||||||
The following standard service parameters can be modified.
|
The following standard service parameters can be modified.
|
||||||
|
@ -75,7 +75,7 @@ static inline size_t request_data_length(MHD_Connection *connection)
|
|||||||
static bool modifies_data(MHD_Connection *connection, string method)
|
static bool modifies_data(MHD_Connection *connection, string method)
|
||||||
{
|
{
|
||||||
return (method == MHD_HTTP_METHOD_POST || method == MHD_HTTP_METHOD_PUT ||
|
return (method == MHD_HTTP_METHOD_POST || method == MHD_HTTP_METHOD_PUT ||
|
||||||
method == MHD_HTTP_METHOD_DELETE) &&
|
method == MHD_HTTP_METHOD_DELETE || method == MHD_HTTP_METHOD_PATCH) &&
|
||||||
request_data_length(connection);
|
request_data_length(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -677,11 +677,11 @@ public:
|
|||||||
m_post.push_back(SResource(new Resource(cb_modulecmd, 4, "maxscale", "modules", ":module", "?")));
|
m_post.push_back(SResource(new Resource(cb_modulecmd, 4, "maxscale", "modules", ":module", "?")));
|
||||||
|
|
||||||
/** Update resources */
|
/** Update resources */
|
||||||
m_put.push_back(SResource(new Resource(cb_alter_server, 2, "servers", ":server")));
|
m_patch.push_back(SResource(new Resource(cb_alter_server, 2, "servers", ":server")));
|
||||||
m_put.push_back(SResource(new Resource(cb_alter_monitor, 2, "monitors", ":monitor")));
|
m_patch.push_back(SResource(new Resource(cb_alter_monitor, 2, "monitors", ":monitor")));
|
||||||
m_put.push_back(SResource(new Resource(cb_alter_service, 2, "services", ":service")));
|
m_patch.push_back(SResource(new Resource(cb_alter_service, 2, "services", ":service")));
|
||||||
m_put.push_back(SResource(new Resource(cb_alter_logs, 2, "maxscale", "logs")));
|
m_patch.push_back(SResource(new Resource(cb_alter_logs, 2, "maxscale", "logs")));
|
||||||
m_put.push_back(SResource(new Resource(cb_alter_maxscale, 1, "maxscale")));
|
m_patch.push_back(SResource(new Resource(cb_alter_maxscale, 1, "maxscale")));
|
||||||
|
|
||||||
/** Change resource states */
|
/** Change resource states */
|
||||||
m_put.push_back(SResource(new Resource(cb_stop_monitor, 3, "monitors", ":monitor", "stop")));
|
m_put.push_back(SResource(new Resource(cb_stop_monitor, 3, "monitors", ":monitor", "stop")));
|
||||||
@ -774,6 +774,10 @@ public:
|
|||||||
{
|
{
|
||||||
return process_request_type(m_put, request);
|
return process_request_type(m_put, request);
|
||||||
}
|
}
|
||||||
|
else if (request.get_verb() == MHD_HTTP_METHOD_PATCH)
|
||||||
|
{
|
||||||
|
return process_request_type(m_patch, request);
|
||||||
|
}
|
||||||
else if (request.get_verb() == MHD_HTTP_METHOD_POST)
|
else if (request.get_verb() == MHD_HTTP_METHOD_POST)
|
||||||
{
|
{
|
||||||
return process_request_type(m_post, request);
|
return process_request_type(m_post, request);
|
||||||
@ -810,6 +814,7 @@ private:
|
|||||||
ResourceList m_put; /**< PUT request handlers */
|
ResourceList m_put; /**< PUT request handlers */
|
||||||
ResourceList m_post; /**< POST request handlers */
|
ResourceList m_post; /**< POST request handlers */
|
||||||
ResourceList m_delete; /**< DELETE request handlers */
|
ResourceList m_delete; /**< DELETE request handlers */
|
||||||
|
ResourceList m_patch; /**< PATCH request handlers */
|
||||||
};
|
};
|
||||||
|
|
||||||
static RootResource resources; /**< Core resource set */
|
static RootResource resources; /**< Core resource set */
|
||||||
@ -820,7 +825,8 @@ static bool request_modifies_data(const string& verb)
|
|||||||
{
|
{
|
||||||
return verb == MHD_HTTP_METHOD_POST ||
|
return verb == MHD_HTTP_METHOD_POST ||
|
||||||
verb == MHD_HTTP_METHOD_PUT ||
|
verb == MHD_HTTP_METHOD_PUT ||
|
||||||
verb == MHD_HTTP_METHOD_DELETE;
|
verb == MHD_HTTP_METHOD_DELETE ||
|
||||||
|
verb == MHD_HTTP_METHOD_PATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool request_reads_data(const string& verb)
|
static bool request_reads_data(const string& verb)
|
||||||
|
@ -6,7 +6,7 @@ function set_auth(auth, value) {
|
|||||||
.then(function(resp) {
|
.then(function(resp) {
|
||||||
var d = JSON.parse(resp)
|
var d = JSON.parse(resp)
|
||||||
d.data.attributes.parameters.admin_auth = value;
|
d.data.attributes.parameters.admin_auth = value;
|
||||||
return request.put(auth + host + "/maxscale", { json: d })
|
return request.patch(auth + host + "/maxscale", { json: d })
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return request.get(auth + host + "/maxscale")
|
return request.get(auth + host + "/maxscale")
|
||||||
|
@ -6,7 +6,7 @@ function set_value(key, value) {
|
|||||||
.then(function(resp) {
|
.then(function(resp) {
|
||||||
var d = JSON.parse(resp)
|
var d = JSON.parse(resp)
|
||||||
d.data.attributes.parameters[key] = value;
|
d.data.attributes.parameters[key] = value;
|
||||||
return request.put(base_url + "/maxscale", { json: d })
|
return request.patch(base_url + "/maxscale", { json: d })
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return request.get(base_url + "/maxscale")
|
return request.get(base_url + "/maxscale")
|
||||||
|
@ -7,7 +7,7 @@ describe("Errors", function()
|
|||||||
|
|
||||||
it("error on invalid PUT request", function()
|
it("error on invalid PUT request", function()
|
||||||
{
|
{
|
||||||
return request.put(base_url + "/servers/server1", { json: {this_is: "a test"}})
|
return request.patch(base_url + "/servers/server1", { json: {this_is: "a test"}})
|
||||||
.should.be.rejected
|
.should.be.rejected
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ describe("HTTP Headers", function() {
|
|||||||
resp.headers.etag.should.be.equal("\"0\"")
|
resp.headers.etag.should.be.equal("\"0\"")
|
||||||
var srv = JSON.parse(resp.body)
|
var srv = JSON.parse(resp.body)
|
||||||
delete srv.data.relationships
|
delete srv.data.relationships
|
||||||
return request.put(base_url + "/servers/server1", {json: srv})
|
return request.patch(base_url + "/servers/server1", {json: srv})
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return request.get(base_url + "/servers/server1", {resolveWithFullResponse: true})
|
return request.get(base_url + "/servers/server1", {resolveWithFullResponse: true})
|
||||||
@ -42,7 +42,7 @@ describe("HTTP Headers", function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
request.put(base_url + "/servers/server1", {json: srv})
|
request.patch(base_url + "/servers/server1", {json: srv})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return request.get(base_url + "/servers/server1", {resolveWithFullResponse: true})
|
return request.get(base_url + "/servers/server1", {resolveWithFullResponse: true})
|
||||||
})
|
})
|
||||||
|
@ -17,7 +17,7 @@ describe("Logs", function() {
|
|||||||
logs.data.attributes.parameters.throttling.suppress_ms = 1
|
logs.data.attributes.parameters.throttling.suppress_ms = 1
|
||||||
logs.data.attributes.parameters.throttling.window_ms = 1
|
logs.data.attributes.parameters.throttling.window_ms = 1
|
||||||
|
|
||||||
return request.put(base_url + "/maxscale/logs", {json: logs})
|
return request.patch(base_url + "/maxscale/logs", {json: logs})
|
||||||
})
|
})
|
||||||
.then(function(resp) {
|
.then(function(resp) {
|
||||||
return request.get(base_url + "/maxscale/logs")
|
return request.get(base_url + "/maxscale/logs")
|
||||||
|
@ -27,7 +27,7 @@ describe("Monitor", function() {
|
|||||||
monitor.data.attributes.parameters = {
|
monitor.data.attributes.parameters = {
|
||||||
monitor_interval: 1000
|
monitor_interval: 1000
|
||||||
}
|
}
|
||||||
return request.put(base_url + "/monitors/" + monitor.data.id, {json:monitor})
|
return request.patch(base_url + "/monitors/" + monitor.data.id, {json:monitor})
|
||||||
.should.be.fulfilled
|
.should.be.fulfilled
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ describe("Monitor Relationships", function() {
|
|||||||
.then(function(resp) {
|
.then(function(resp) {
|
||||||
var mon = JSON.parse(resp)
|
var mon = JSON.parse(resp)
|
||||||
delete mon.data.relationships.servers
|
delete mon.data.relationships.servers
|
||||||
return request.put(base_url + "/monitors/MySQL-Monitor", {json: mon})
|
return request.patch(base_url + "/monitors/MySQL-Monitor", {json: mon})
|
||||||
})
|
})
|
||||||
.should.be.fulfilled
|
.should.be.fulfilled
|
||||||
});
|
});
|
||||||
@ -69,7 +69,7 @@ describe("Monitor Relationships", function() {
|
|||||||
{id: "server3", type: "servers"},
|
{id: "server3", type: "servers"},
|
||||||
{id: "server4", type: "servers"},
|
{id: "server4", type: "servers"},
|
||||||
]
|
]
|
||||||
return request.put(base_url + "/monitors/" + monitor.data.id, {json: mon})
|
return request.patch(base_url + "/monitors/" + monitor.data.id, {json: mon})
|
||||||
})
|
})
|
||||||
.should.be.fulfilled
|
.should.be.fulfilled
|
||||||
});
|
});
|
||||||
@ -80,7 +80,7 @@ describe("Monitor Relationships", function() {
|
|||||||
.then(function(resp) {
|
.then(function(resp) {
|
||||||
var mon = JSON.parse(resp)
|
var mon = JSON.parse(resp)
|
||||||
delete mon.data.relationships.servers
|
delete mon.data.relationships.servers
|
||||||
return request.put(base_url + "/monitors/" + monitor.data.id, {json: mon})
|
return request.patch(base_url + "/monitors/" + monitor.data.id, {json: mon})
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return request.get(base_url + "/monitors/MySQL-Monitor")
|
return request.get(base_url + "/monitors/MySQL-Monitor")
|
||||||
@ -93,7 +93,7 @@ describe("Monitor Relationships", function() {
|
|||||||
{id: "server3", type: "servers"},
|
{id: "server3", type: "servers"},
|
||||||
{id: "server4", type: "servers"},
|
{id: "server4", type: "servers"},
|
||||||
]
|
]
|
||||||
return request.put(base_url + "/monitors/MySQL-Monitor", {json: mon})
|
return request.patch(base_url + "/monitors/MySQL-Monitor", {json: mon})
|
||||||
})
|
})
|
||||||
.should.be.fulfilled
|
.should.be.fulfilled
|
||||||
});
|
});
|
||||||
|
@ -38,7 +38,7 @@ describe("Server", function() {
|
|||||||
|
|
||||||
it("update server", function() {
|
it("update server", function() {
|
||||||
server.data.attributes.parameters.weight = 10
|
server.data.attributes.parameters.weight = 10
|
||||||
return request.put(base_url + "/servers/" + server.data.id, { json: server})
|
return request.patch(base_url + "/servers/" + server.data.id, { json: server})
|
||||||
.should.be.fulfilled
|
.should.be.fulfilled
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ describe("Server Relationships", function() {
|
|||||||
|
|
||||||
it("remove relationships", function() {
|
it("remove relationships", function() {
|
||||||
delete rel_server.data["relationships"]
|
delete rel_server.data["relationships"]
|
||||||
return request.put(base_url + "/servers/" + rel_server.data.id, {json: rel_server})
|
return request.patch(base_url + "/servers/" + rel_server.data.id, {json: rel_server})
|
||||||
.should.be.fulfilled
|
.should.be.fulfilled
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ describe("Service", function() {
|
|||||||
.then(function(resp) {
|
.then(function(resp) {
|
||||||
var svc = JSON.parse(resp)
|
var svc = JSON.parse(resp)
|
||||||
svc.data.attributes.parameters.enable_root_user = true
|
svc.data.attributes.parameters.enable_root_user = true
|
||||||
return request.put(base_url + "/services/RW-Split-Router", {json: svc})
|
return request.patch(base_url + "/services/RW-Split-Router", {json: svc})
|
||||||
})
|
})
|
||||||
.then(function(resp) {
|
.then(function(resp) {
|
||||||
return request.get(base_url + "/services/RW-Split-Router")
|
return request.get(base_url + "/services/RW-Split-Router")
|
||||||
@ -25,7 +25,7 @@ describe("Service", function() {
|
|||||||
.then(function(resp) {
|
.then(function(resp) {
|
||||||
var svc = JSON.parse(resp)
|
var svc = JSON.parse(resp)
|
||||||
delete svc.data.relationships
|
delete svc.data.relationships
|
||||||
return request.put(base_url + "/services/RW-Split-Router", {json: svc})
|
return request.patch(base_url + "/services/RW-Split-Router", {json: svc})
|
||||||
})
|
})
|
||||||
.then(function(resp) {
|
.then(function(resp) {
|
||||||
return request.get(base_url + "/services/RW-Split-Router")
|
return request.get(base_url + "/services/RW-Split-Router")
|
||||||
@ -51,7 +51,7 @@ describe("Service", function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return request.put(base_url + "/services/RW-Split-Router", {json: svc})
|
return request.patch(base_url + "/services/RW-Split-Router", {json: svc})
|
||||||
})
|
})
|
||||||
.then(function(resp) {
|
.then(function(resp) {
|
||||||
return request.get(base_url + "/services/RW-Split-Router")
|
return request.get(base_url + "/services/RW-Split-Router")
|
||||||
|
Reference in New Issue
Block a user