Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multiple schemas in one instance #1450

Merged
merged 45 commits into from
Mar 30, 2020

Conversation

steve-chavez
Copy link
Member

Continuing the work on #1436.

@steve-chavez
Copy link
Member Author

@MahmoudKassem Regarding your issue on #1436 (comment)

implement a check that forbids cross schema queries to go through unless both source and target schema are explicitly added in the db-schema parameter

What I've done is to reject the request at the http level(it doesn't hit the database, so no need for tailoring an invalid sql query). I've taken into account the Accept-Profile proposal recommendations:

If the server cannot process the specified profile, it would answer with an http 406 status code and possibly a list of acceptable profiles.

So, if the user specifies an invalid schema he'll get an error:

curl -H "Accept-Version: v5" localhost:3000/table
{"message":"The schema must be one of the following: v1, v2"} 

I've also solved the dbstructure issue(mentioned in #1436 (comment)).

Let me know what you think.

@ghost
Copy link

ghost commented Feb 26, 2020

@steve-chavez great job! Now all that's left is the OpenAPI test, right? Will try to get them done tomorrow.

@steve-chavez
Copy link
Member Author

steve-chavez commented Feb 27, 2020

@MahmoudKassem I've noticed that with the Accept-Version approach we can GET different schemas resources. But how about a POST or PATCH to a different schema resource?
With the current interface that cannot be done.

(Edit: Actually, I've noted that it's possible to do this with the same Accept-Version header, but doing it this way wouldn't be complying to http semantics. Unless we add a Content-Version ourselves.)

However If we fully adopt the Content Negotiation by Profile spec we could have that working by doing:

POST /items
Content-Profile: v2 

{....}

That would make an insert to the v2.items relation.
I'll have to read the spec more carefully and see if we can do it that way.

For now, you adding the missing OpenAPI tests would be great.

@steve-chavez
Copy link
Member Author

steve-chavez commented Mar 2, 2020

To fully adopt the profile negotiation spec, we would need to implement two requests: list profiles and get resource by profile.

I'm asking some questions to the workgroup(w3c/dx-connegp#15 and w3c/dx-connegp#18) for how to do this correctly.

As it is now, we can implement only the "get resource by profile" part(the Accept-Profile/Content-Profile requests, which would solve our issue).

I'd like to move forward and make a new release. We can work on supporting "list profiles" after the release and for now we'll make a note in the docs about it being a work in progress.

@ghost
Copy link

ghost commented Mar 2, 2020

@steve-chavez the test is failing because it is omitting the "value" object in the properties as well as the "value" key in the "required" array, not sure what's wrong.

test_error

@ghost
Copy link

ghost commented Mar 4, 2020

@steve-chavez in my local database(not the one created for the tests) the fields where marked as not null that's why they were showing up in the required field when calling the service with curl. Now that error is gone, but I don't understand why the values are omitted under "properties".

@steve-chavez
Copy link
Member Author

steve-chavez commented Mar 4, 2020

I also don't have a clue why the value in properties might be missing. It's weird because when running postgrest executable I get the correct openapi output but on the test suite the value is missing.

@steve-chavez
Copy link
Member Author

My only guess for now is that the DbStructure doesn't contain the columns(value) but it does contain the primary keys and somehow this is only happening when running the test suite. There must be something related to the config there. I'd have to traceShowId to see what's going on.

@steve-chavez
Copy link
Member Author

Ahhh.. here's the culprit:

postgrest/test/Main.hs

Lines 58 to 60 in de218e9

result <- P.use pool $ do
ver <- getPgVersion
HT.transaction HT.ReadCommitted HT.Read $ getDbStructure "test" ver

In the test suite the DbStructure is only loading the "test" schema. This needs refactoring so it doesn't happen again when testing other schemas.

@ghost
Copy link

ghost commented Mar 4, 2020

@steve-chavez thank you for finding the issue, now that this is addressed its time to take a look at "get resource by profile" implementation.

@steve-chavez
Copy link
Member Author

steve-chavez commented Mar 5, 2020

@MahmoudKassem Regarding "get resource by profile", for GET/HEAD should be simple. Basically:

GET /table
Accept-Profile: v1

HTTP/1.1 200 OK
Content-Profile: v1

body here

That is, when Accept-Profile is specified the response must contain a Content-Profile reply if it's possible to comply.

For POST/PATCH/PUT/DELETE, the implementation should be:

POST /table
Content-Profile: v1

body here

HTTP/1.1 201 Created

That should also be straightforward but there's a catch here.

Suppose we have a private.table, and we create a VIEW of this table in v1.table, another VIEW in v2.table and we expose these v1, v2 schemas through postgrest.

Should we allow to POST a payload to a v1.table and respond with a v2.table representation? In theory, this could be done with the following headers:

POST /table
Content-Profile: v1
Accept-Profile: v2
Prefer: return=representation

body here

For now I think we should not allow this and keep it simple. If a POST request with Content-Profile, Accept-Profile and Prefer: return=representation comes, assume the Content-Profile schema(ignore Accept-Profile) and add the Content-Profile: v1 header to the response as well.

Mahmoud Kassem and others added 13 commits March 13, 2020 14:01
…mportant to note that the db-schema can still be used as usual what has been added is the possibility to query different schemas by specifying them in the uri postgrest-host:postgrest-port/schema/table. Another important note is that the extensions only includes the functionality itself the OpenAPI generation is only available for the schema specified in db-schema
…ed by comma. The dynamic schema is now passed via header instead of uri and it has to be one of the values of db-schema otherwise the default schema will be used which is the first value of db-schema.
… header. Inputing a schema not existing in th db-schema configuration parameter will now result in an 404 Not Found instead of falling back to the default schema which is the first value of db-schema.
@steve-chavez
Copy link
Member Author

Just a note here, on how to implement the "list profiles" part.

First we would need a way to get the metadata for a single resource. Right now we offer metadata for all resources at the root. This was also requested in #1421.

I'm thinking that the link for getting a v1.table metadata should be:

GET /?resource=table&_profile=v1

(Borrowing the _profile query param from the spec)

This would give a filtered openapi output. Basically, the openapi paths, definitions and parameters objects would only contain info related to v1.table instead of containing the whole schema info.

Once we have that, "list profiles" would be done with OPTIONS:

OPTIONS /table

Accept: application/json, ...
Accept-Profile:
  /?resource=table&_profile=v1;
          rel="canonical";
          token="v1",
  /?resource=table&_profile=v2;
          rel="alternate";
          token="v2"

(I'm using relative URLs there, but they could be absolute like <http://example.org/?_profile=v2&resource=table>)

Once we have that I think we would be in compliance with the spec. It can be done in a later PR after #1421 is solved.

@steve-chavez
Copy link
Member Author

Come to think of it, if we list profiles with OPTIONS that would also solve #790. Funny that #790 (comment) also mentioned the profile spec as a possible solution. Though it was an older one where the profile could only be specified in the media type.

@steve-chavez
Copy link
Member Author

I've added a condition for checking profile negotiation is only possible when multiple schemas are specified. This is to ensure that users that would like to keep using a single schema don't see the Content-Profile: v1 response.

Tests that are missing: POST/PATCH/PUT/DELETE, calling a stored proc and maybe a resource embedding one to ensure it all works normally.

@steve-chavez
Copy link
Member Author

I think I've finished my work here. The tests should cover all of the http verbs.

@MahmoudKassem Could you give it a final review and add an entry to the CHANGELOG?

@ghost
Copy link

ghost commented Mar 28, 2020

@steve-chavez Everything looks fine to me, sorry for being so inactive things have been crazy lately and thank you for all your efforts, patience and all that I have learned. Glad to see this feature being seen through. I've added an entry to CHANGELOG.md , if you want to add something please feel free to do so.

@steve-chavez
Copy link
Member Author

@MahmoudKassem Thanks a lot for your work and help here. Without your initiative this feature wouldn't have been done by now. So I'm also giving you credit on the CHANGELOG.

I'll merge and make a new release 🚀

@steve-chavez steve-chavez merged commit 691bb56 into PostgREST:master Mar 30, 2020
vwwv pushed a commit to on-ramp/postgrest that referenced this pull request Apr 20, 2020
The schema to use can be selected through the headers `Accept-Profile` for GET/HEAD and `Content-Profile` for POST/PATCH/PUT/DELETE.

This is based on the https://www.w3.org/TR/dx-prof-conneg/ttps://www.w3.org/TR/dx-prof-conneg/ spec.

Also increase all memory tests by 1M(otherwise CI fails).

Co-authored-by: Mahmoud Kassem <[email protected]>
Co-authored-by: Mahmoud Kassem <[email protected]>
monacoremo pushed a commit to monacoremo/postgrest that referenced this pull request Jul 17, 2021
The schema to use can be selected through the headers `Accept-Profile` for GET/HEAD and `Content-Profile` for POST/PATCH/PUT/DELETE.

This is based on the https://www.w3.org/TR/dx-prof-conneg/ttps://www.w3.org/TR/dx-prof-conneg/ spec.

Also increase all memory tests by 1M(otherwise CI fails).

Co-authored-by: Mahmoud Kassem <[email protected]>
Co-authored-by: Mahmoud Kassem <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants