Skip to content
This repository has been archived by the owner on Mar 18, 2019. It is now read-only.

Best way to handle "application/problem+json" #114

Closed
danballance opened this issue Feb 6, 2017 · 8 comments
Closed

Best way to handle "application/problem+json" #114

danballance opened this issue Feb 6, 2017 · 8 comments

Comments

@danballance
Copy link

Hi there,

The framework I am using (https://github.com/zalando/connexion) returns validation errors as Content-Type "application/problem+json" (https://tools.ietf.org/html/rfc7807) and this generates an error for me using Core API:

coreapi.exceptions.NoCodecAvailable: Unsupported media in Content-Type header 'application/problem+json'

As a kind of hackish test, I have created a file jsonproblem.py to make available a new codec JSONProbemCodec which does nothing apart from provide a different media type:

# coding: utf-8
from coreapi.codecs.jsondata import JSONCodec

class JSONProblemCodec(JSONCodec):
    media_type = 'application/problem+json'

So my question is two-fold really.

  1. Am I going about this the right way - is this the best way to add support for a very simple new JSON media type?
  2. If this is the way to go, would you accept a PR into this repository or should I create an independant pip package (which seems like overkill to me)?

Any advice appreciated. I'm keen to recommend coreapi as a simple python interface for our customers to use with our new API, but I just need to iron a few teething issues first. This is the first issue I have hit during testing.

@tomchristie
Copy link
Contributor

I wouldn't create a new pip package, or PR into this repo, but you would need have a custom codec to deal with 'application/problem+json', yes.

from coreapi import codecs

# A custom codec to handle 'application/problem+json' responses.
class ProblemJSON(codecs.JSONCodec):
    media_type = 'application/problem+json'

# Instantiate our client with set of codecs we want to support.
decoders = [
    codecs.CoreJSONCodec(),  # Or whatever you use for your API schemas.
    codecs.JSONCodec(),          # For standard responses
    ProblemJSON()                     # For error responses
]
client = Client(decoders=decoders)

It's possible that we could consider including 'application/problem+json' by default, but I don't think that's necessarily something we'd want to do just yet.

@tomchristie
Copy link
Contributor

  • This that resolve things adequately, or is that too awkward for your needs?
  • Out of interest, what media type are you exposing your API schemas?
  • Any pain points, or general points of confusion, with using coreapi as a client library?

Thanks, Dan!

@danballance
Copy link
Author

danballance commented Feb 7, 2017

Okay thanks, that will work fine I guess. Although It's not going to be quite as "plug-and-play" for our customers then because I'm going to need to provide an additional decoder class before they can access our API properly. So I guess I will lean towards providing an example client that they can install from pip/github that sets CoreAPI up correctly for them with this additional class.

In terms of non-error responses I am returning application/hal+json hypermedia resources. For the API schema itself I'm using OAI/Swagger2. I have built a kind of in-house schema on top of these formats that we're using internally to automate a lot of the repetitive boiler plate required for many API operations. It's working pretty nicely so far - lots of automatic code generation of the API server code as well as client libraries such as Core API working, pretty much, out of the box.

There are always one or two teething issues to solve though. The next one actually is authentication. My API is using the OAuth2 password flow. Are there any plugin options for authentication or is this something I'll need to add myself?

@tomchristie
Copy link
Contributor

Although It's not going to be quite as "plug-and-play" for our customers

On second thoughts maybe we could provide this built-in. (Or something like have the default decoders automatically be whatever plugins are available, so that it's only a pip install away, and nothing else)

The next one actually is authentication. My API is using the OAuth2 password flow. Are there any plugin options for authentication or is this something I'll need to add myself?

At the moment the best you'd get would be adding the auth endpoints to the scheme, tho you'd still need to create a new client once you do have the auth token. And yes, addressing this is absolutely on the list.

@danballance
Copy link
Author

danballance commented Feb 7, 2017

Thinking about this a little further, maybe for now I will go down the route of a separate pip package then - even though the code will be ridiculously trivial. As you say, it's just a pip install away and I can still point people directly to this project and just say in the docs that they need to ensure they install a specific list of pip packages with it.

Edit: I'm here actually meaning a pip package that can be installed from github - that probably I'll just include in a requirements.txt file. I'm not meaning an official pip package.

In terms of authentication, since this PR in the Swagger-UI project my users can now authenticate directly in Swagger-UI when they are exploring the API. (Which is pretty cool!) I'll have to have a think about what I can do here in the meantime in terms of authenticating our customers with CoreAPI - obviously authenticating to the API is an absolutely critical feature. Is it worth me opening an issue for authentication plugins or do you have this covered off elsewhere?

thanks for your help and prompt replies!

@tomchristie
Copy link
Contributor

Is it worth me opening an issue for authentication plugins or do you have this covered off elsewhere?

Opening the issue makes sense, yup.

There's a coupla different issues worth talking through there:

  • What set of auth policies might we need to support?
  • What does an example auth flow look like?
  • What information does the scheme itself need to present in order to make that possible?

@danballance
Copy link
Author

In terms of workarounds or interim solutions, I've noticed that I can pass in credentials to the client via the transports list. This is just a simple test, but this authenticates me okay to my local dev server.

transports = [
    HTTPTransport(credentials={"localhost": "Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"})
]
client = Client(decoders=decoders, transports=transports)

Is there any chance you could explain a little more about the credentials variable/attribute and the purpose behind the host check? I'm just wondering if I can find a way to piggy back on this somehow. Perhaps the token generation and caching could be done outside of CoreAPI for now and the client is then instantiated with a transport that has a bearer token ready to go.

It's not ideal but it might be okay as a workaround until #115 is complete.

@danballance
Copy link
Author

Final comment for the day from me :)

So I've decided to import a set up function that makes things nice and easy for our customers. This particular use case is for the example/tutorial section from our API docs and for this section at least I want things to be really easy without needing to understand too much about how things are working internally. I end up with example code like the following that seems to work for my purposes:

from orbtalk_api_utilities import setup_coreapi
client, schema = setup_coreapi(auth_endpoint, auth_username, auth_password, schema_url)

print "Creating new call for extension 1000..."
doc_call = client.action(schema, ['pbx.Call', 'resources.pbx.call.create'], params={
    "call": {
        "domainId": domain,
        "origExtension": extension,
        "destination": destination
    }
})
print "...create call result was:"
print doc_call

Happy days.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants