How to create a good PATCH endpoint when some properties can change and some can't based on the status of the resource? - rest

Consider I have the following Resource:
public class Foo
{
public string Text {get; set;}
public int ReminderInDays {get; set;}
// Determined by resource itself
public FooStatus Status {get;set;}
}
Now, let's say we want to be able to update these. Normally, you could create a PATCH endpoint:
PATCH api/foos/1 with a body that contains both Text and ReminderInDays. (I do not use stuff like Jsonpatch where you can submit only the changed properties).
This would work fine. But how about the following scenario:
Text can change when Status is New and Submitted
ReminderInDays can change when Status is New
If I were to use my PATCH example, I could use a body like the following when status is New, and everything would be OK:
{
"Text": "Hello World!",
"ReminderInDays": 3
}
But if I were to use this when the status of the resource is Submitted and I would change ReminderInDays to 5, the Resource would throw an error because I am not allowed to change that property anymore. Based on this information, the only way I can think of having 1 endpoint to change both, is to ignore the ReminderInDays property when the Resource tells me the property can't be changed.
This seems like an ugly solution to me. You need to submit useless data to the API which is a waste.
Another solution is to have 2 endpoints:
PATCH api/foos/1/text
PATCH api/foos/1/reminderInDays
This isn't really RESTFUL, but would solve my issues.
Does anyone have any other ideas? What is the right thing to do :)?

Related

Why am I not getting 'Warning'-s included in my Route from the Bing Maps API

The Bing Maps API can include 'Warning' information as part of each ItineraryItem - things like TrafficFlow, UnPavedRoad, and so forth.
However, these warnings don't make it as part of the returned route.
To recreate:
In the REST Service Toolkit Test app, change the route waypoint(s) in the RouteBtn_Clicked routine to reflect a route that will have a Warning included. (Test by routing it on maps.bing.com - things like crossing a state boundary, traffic congestion, unpaved road, etc.). Note that there are no Warning-s in the returned Route. However, if you paste the raw REST call in your browser, you will see the warning information included in the raw output.
It turns out there's an error in the JSON Contract provided by Microsoft.
If you change (in the RestServiceModels.cs):
[DataMember(Name = "warning", EmitDefaultValue = false)]
public Warning[] Warning { get; set; }
To (note the s at the end of the Name)
[DataMember(Name = "warnings", EmitDefaultValue = false)]
public Warning[] Warning { get; set; }
Then it all works as expected.

WEB API 2 how to set Content-Type server side?

I have a customer that has a specific API design I am required to comply with. The logic I host with my WEB API code allows the customer to make simple changes to a resource that exists on my system (change, delete etc.).
The interface is very simple:
public IHttpActionResult Post(OpRequest opRequest)
public class OpRequest
{
public string op { get; set; }
public string data { get; set; }
}
Based on the value of "op", i parse "data" to complete the operation.
My question is related to the Content-type header in their request. They do not send a Content-Type header at all, but the actual data they POST is "application/x-www-form-urlencoded" for some requests and "application/json". for other requests. Works fine when they send urlencoded, but throws "415 unsupported media" error when they send JSON.
My thought is that I need to intercept their request, detect the content-type and set it before it reaches my logic, but I am not certain how to do that. I must use a single operation to accommodate all Content-types. Is this possible?

How do I access SOAP headers in a Spyne srpc method?

Forgive me if I am just being thick but the Spyne documentation on headers seems a bit thin and the link to the sample code is broken. I think I found the sample code here but it only seems to access the headers in a listener callback function. I need access to it in the srpc method if possible.
What I would like to do is something like this (this does not work):
class MyRequest(ComplexModel):
Messages = Array(Unicode)
class MyHeader(ComplexModel):
TrackingID = ByteArray
class MySoapService(ServiceBase):
#srpc(MyRequest, _in_header=MyHeader)
def PostMessages(req, hdr):
logging.info(u'RECEIVED: {0:s}'.format(hdr.TrackingID))
If it helps, I am trying to replace a service written in .NET that just defines the message headers in the message contract like this:
[MessageContract]
public class MyRequest
{
[MessageHeader]
public Guid TrackingID { get; set; }
[MessageBodyMember]
public String[] Messages { get; set; }
}
You can't read header data from within an srpc function. #srpc is for existing functions that you don't want to touch. You should always use #rpc unless you know you need #srpc
Decorate your function using #rpc. This passes the rpc context (conventionally named ctx) as first argument to your function. Then, ctx.in_header is where you'll find the header data. You can also set ctx.out_header to alter outgoing response headers.
Also, pull requests for docs are pure gold to me :)

RESTful resource with an expensive property

I have a resource /messages.
In my java code, the Message interface looks like this:
interface Message {
public int getID();
public int getPostingUserID();
public String getText();
public int getRelevance();
}
For example, /messages/7 will give the following:
{ id: 7, postingUserID: 102, text: "tabs are better!", relevance: 10.3 }
Unfortunately, getRelevance() is quite expensive to calculate on-the-fly, and is only needed for perhaps a tenth of the queries. I don't want to calculate it when I don't need it.
So, I can see a few options:
/messages/7?includeRelevance=true to tell it to calculate the relevance, otherwise don't include it in the response
/messages/7/relevance as a separate request, and calculate it then
/relevances?forMessageID=7 as a separate request, and calculate it then
Which option is the most RESTful?
Thanks!
I would tend to go for /messages/7/relevance in this situation, and I'd make sure that the document returned when fetching /messages/7 includes a URL to the relevant relevance resource (for HATEOAS discoverability, of course).
The most RESTful way to do this (I believe, and could definitely be wrong) is to define 2 different media-types.
The client can request a specific one with the Accept: header, and you could default to the minimal one.

Response and DTO objects missing from XSD

I'm using the latest version of ServiceStack with NuGet. I've got a basic service setup that works fine with the JsonServiceClient and is passing all of our unit tests as expected.
Unfortunately I'm also trying to support SOAP and Visual Studios "Add Service Refernece" proxy generation for interop with other groups who want to continue using that sort of framework.
What I'm running into is that our DTO's including the Response objects are not being populated into the XSD's or wsdl's that ServiceStack is generating via the metadata page. This seems very odd in conjunction with the fact that when viewing the individual SOAP Operation examples the correct Response and DTO's are shown in the generated example.
An Example Response:
Namespace Operations
<DataContract>
Public Class GetItemResponse
Implements IHasResponseStatus
<DataMember>
Public Property ResponseStatus As ResponseStatus Implements IHasResponseStatus.ResponseStatus
<DataMember>
Public Property Item As Item
End Class
End Namespace
An Example DTO:
Namespace Types
<DataContract>
Public Class Item
<DataMember>
Public Property ItemIdentifier As String
<DataMember>
Public Property ItemId As Guid?
<DataMember>
Public Property ItemName As String
<DataMember>
Public Property Description As String
<DataMember>
Public Property InstallDate As DateTime?
<DataMember>
Public Property FeatureNumber As String
<DataMember>
Public Property ModelNumber As String
End Class
End Namespace
I'm using the Assembly: ContractNamespace attribute to set the XML namespace as well.
I've downloaded the source code and done some investigation on my own. I'm not sure how recent this change is but it appears that in order for your DTO's and Response objects to be included in the wsdl you need to add a query string like "?includeAllTypes=true".
I'm also using the ServiceStack BasicAuthProvider setup which is causing AssignRoles and UnAssignRoles to be added to the Service automatically. The request and response objects for those calls are still failing to make it into the wsdl and causing "Add Service Reference" to fail. Fortunately we aren't making use of them so if I can find another configuration setting to remove them all should be working correctly.