Map keys must be unique in swagger - rest

I'm using swagger YAML for documenting few APIs. The APIs that I have are as follows:
POST: /customers/id
GET: /customers/id
PUT: /customers/id
DELETE: /customers/id
I'm using the same name for the APIs but I'm differentiating it based on the type of http call as per the REST URI naming convention. But when documenting this using swagger, I'm getting "Map keys must be unique" error.
How can I overcome this error?

If you use OpenAPI (Swagger) you should write it like this:
paths:
/customers/id:
post:
...
get:
...
put:
...
delete:
Unfortunately, OpenAPI is not convenient in this case, because usually there are many lines of code between method names (post, get, put, etc.). So when you look at some specific place in your code, it is not easy to say, what endpoint you are actually looking at. Thus I understand your wish to write it like this: POST: /customer/id: it is more natural and user-friendly, but not possible in OpenAPI.
However, you can use JSight, which does just the thing you want. Look, it is much easier to grasp:
JSIGHT 0.3
POST /customers/{id}
GET /customers/{id}
PUT /customers/{id}
DELETE /customers/{id}
I have written a more detailed example for you here: https://editor.jsight.io/r/aL952LZ/1

Related

Best practice for including common http error codes in swagger / openapi definition

I'm wondering about good practices about including common error types in Swagger/OpenAPI definition.
Let's consider following controller method:
[HttpGet]
[ProducesResponseType(StatusCodes.Status400BadRequest)] // to be or not to be?
public ActionResult SomeMethod(Foo foo)
{
if (foo.Property != "expectedValue")
{
return BadRequest();
}
return Ok();
}
So, I'm performing some logic in the controller, which might end up in a state in which I want to return 400 BadRequest. Note, that I don't return any content.
Since I develop a REST API that generates Swagger/OpenAPI definition and tools like autorest may be used to generate client code based on that definition, I want to be sure it is as accurate as possible.
My question is:
should I declare explicitly that the user might get 400 Bad Request
OR
this makes sense only in case I want to describe the format of response content (in case 400Bad request has some content)
The same applies to codes like 401, 404 etc.
Even if (in addition to the returned http code, 400, 401, etc..) you do not return any payload at all, you should yet explicitly declare such responses.
Do not forget that in a "code-first" approach, your build chain could automatically generate the OpenApi contract (based on your code) as well as a nice user documentation.
In the contrary approach (contract-first), the code generated based on your OpenApi file would contain these response descriptions.
If you plan to use client code generation, then it's always better to use these declarations, because code generators (like autorest or NSwag) can use this information (usually from a swagger definition aka swagger.json) to generate more correct responses and error handling procedures.
Here, for example, you can see what can happen if you omit such declarations.
Also, remember, that in API controllers you can use web API conventions.
It says that these conventions allow you to:
Define the most common return types and status codes returned from a specific type of action.
Identify actions that deviate from the defined standard.
So, generally speaking, they can help you to make your code more concise.
The object BadRequest is used as a response (Result) with StatusCode=400.
DotNet has several response types with predefined StatusCode, so you can use different methods, for example:
NotFound(404):https://learn.microsoft.com/en-us/dotnet/api/system.web.http.apicontroller.notfound?view=aspnetcore-2.2
InternalServerError(500):https://learn.microsoft.com/en-us/dotnet/api/system.web.http.apicontroller.internalservererror?view=aspnetcore-2.2
Or simply use StatusCode for any other code that is not predefined:https://learn.microsoft.com/en-us/dotnet/api/system.web.http.apicontroller.statuscode?view=aspnetcore-2.2

Retrofit2 - How to execute a JSON Patch Request (RFC 6902)

As of Retrofit2 2.3.0 there seems to be no built in functionality to execute a JSON Patch Request (as defined in RFC 6902. Also, see http://jsonpatch.com/ for some examples).
Using the available #PATCH annotation, the full blown object is sent with the request (as if I would send a PUT request, which is not what I'm looking for)
public interface MyService {
#PATCH("example/{id}")
Call<Example> patchExample(#Path("id") String id, #Body Example example);
}
After a first glance at the Retrofit documentation, there seems to be no clean and easy way to introduce a custom annotation (e.g. #JSONPATCH) to have my own implementation working.
The only related information I was able to find regarding this requirement was this experimental (as he calls it himself --> this is very experimental but it does the job currently) approach at https://medium.com/#andretietz/custom-annotations-with-retrofit-2-8701ca7ce102. I didn't give this example a try, but the complexity seems a little bit out of scale for this simple requirement.
Maybe I'm missing something and there is an easy solution for this?

Manipulating path mapping in AWS API gateway integration

I would like to modify an url parameter /resource/{VaRiAbLe} in an API gateway to S3 mapping so that it actually points to /my-bucket/{variable}. That is, it accepts mixed-case input, and maps it to a lower-case name. Mapping path variables is relatively simple enough to S3 integrations, but I can't seem to get a lower-case mapping working.
Reading through the documentation for mapping parameters, it looks like the path parameters are simple string values (and not templated values), and so defining a mapping as method.request.path.variable.toLowerCase() won't work.
Does anyone have any ideas how to implement this mapping?
Map path variables to a JSON body, and then call another API method that actually does the S3 call?
Bite the bullet, and implement a Lambda function to do the S3 get for me?
Find another api method for S3 that accepts a JSON body that I can use to get the data?
Update using Orchestrated calls
Following the info from Jack, I figured I should try doing the orchestrated call, since the traffic volume is low enough that I am sure that I won't be able to keep the lambda hot.
As a proof of concept, I added two methods to my resource (sitting at /resource/{variable} - GET and POST. The GET method chains to the POST, which does the actual retrieving of the data.
POST method configuration
This is a vanilla S3 proxying method, where you set the URL Path parameter for {variable} to be method.request.body.variable.
GET method configuration
This is a HTTPS proxying method. You'll need an URL for the POST method, so you'll need to deploy the API to get the URL. The only other configuration needed here is a body mapping template with content like:
{
"variable" : "$input.params('variable').toLowerCase()",
"something" : "$input.params('something')"
}
This should be enough to get this working.
The downside to this looks to be that I'm adding an extra method (POST) to my API for that resource that could confuse consumers of the API. I think it should be possible to make the POST on the /resource resource, which would at least make a bit more sense from an API design standpoint.
Depending on how frequently this API will be called, I'd either go with the Lambda proxy or chaining two API Gateway methods together. If the API is called frequently enough to keep a Lambda function warm (say once a minute), then go with Lambda. If not, go with the orchestrated API call.
The orchestrated API call would be interesting, I'd be happy to help with that if you have questions.
As far as I know the only S3 API for getting object data is the GET that is documented in their API reference.

What REST action should I use for Validate?

I have a design question on REST. I need to expose a validate method as a rest resource. Let us say it looks like this
public ValidatedResult validate(ObjectToBeValidated object)
ObjectToBeValidated is a class that contains the actual Object and also some parameters concerning the validation.
When I design this as a Restful resource, which action do I use? From my understanding GET is the action that best suits this case. If that is so, how I can pass my ObjectTobeValidated as an object but not as URL parameters? I shy away from URL parameters because ObjectToBeValidated may contain a lot of properties, ending up with an URL like below which is feel is too long
http://localhost/rest/validate?prop1=somevalu&prop2=somevalue&prop3=something&prop11=somevalu&prop22=somevalue&prop33=something
Any help would be appreciated
Thanks
Kay
The HTTP standard allows you to use the POST method. It does not necessary need to have a side effect.
The action performed by the POST method might not result in a resource
that can be identified by a URI. In this case, either 200 (OK) or 204
(No Content) is the appropriate response status, depending on whether
or not the response includes an entity that describes the result.
HTTP 1.1 / method definitions / POST
In your case you can do something like this if you want to follow the noun-verb approach Tim suggested:
POST /api/my/object/validator
Be aware that by REST the messages must be self-descriptive, so either you need a vendor MIME type or you need to add meta-data e.g. RDF to describe what this link does and what params are allowed. Otherwise we are not talking about REST, just a regular webapp.

What is a 2-dimensional key-value format that Api Blueprint can understand?

I'm developing api documentation for a RESTful search API using Api Blueprint. I would like to be able to pass filters to the API so I can assemble:
filter[filtername1]=filtervalue1
filter[filtername2]=filtervalue2
Per this question, I'm using percent encoded square brackets, but unlike this question, it's not possible for us to describe every possible key name:
How to format hash-based parameters in the URL when creating Blueprint API doc?
I want the key name to be variable, since it could be any field in the source data. Does this work?
## Key-Value-Test [/api/v1/keyvaluetest?term={term}&filter%5B{field_name}%5D={field_value}]
+ term
+ filter_field
+ filter_value
Is there a recommended format for a two-dimensional array like this? It doesn't seem like this would work in Dredd because + filter_field doesn't really match filter[filter_field]
I am afraid that API Blueprint and Apiary does not yet allow these kind of dynamic URL definitions.
API Blueprint and Apiary only allows URI Templates as defined in RFC 6570
The following URI Template is not valid according to that RFC
GET /resource?year={year}&month={month}
You can change the URL to define something like the following:
## Key-Value-Test [/api/v1/keyvaluetest{?term,field_name,field_value}]
+ Parameters
+ term: a
+ field_name: b
+ field_value: c
There are two caveats with this method:
You can only give one field name and field value for the parameters. If you want more field parameters, you have to extend the URL.
You have to change the API url which I don't think you would want to.
Please start a feature request at http://support.apiary.io if you have any.
API Blueprint uses URI Templates standard. There are ways to express and expand arrays (see section 3.2.1), however, it expects "standard URI approach", meaning the URI would be expanded as follows:
/api/v1/keyvaluetest?term=yourterm&filter=filtervalue1&filter=filtervalue2
which is a "standard" way of doing arrays, except the most popular web language popularised your way back in 2000s.
The templates are designed for expansion: give it a bunch of variables and a string, and you'll get a properly-formatted string. As far as I am aware, there is no "wild match" (inserting pattern-match variables at a certain position in string).
The only solution I can think of within the realm of URL templates would be taking advantage of explosion modifier (see composite values):
/api/v1/keyvaluetest{?keys*}
which, given associative array of values [(filter%5Bfiltername1%5D, filtervalue1), (filter%5Bfiltername2%5D, filtervalue2) ] should expand properly.
However, I am not sure how to specify those in MSON as I don't think there is a support for "dynamic keys" and I think most of the tooling wouldn't handle it (yet).
Might be worth asking.