-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
WIP - add raw-media-endpoints config option #1546
Conversation
15d2405
to
456af56
Compare
requestContentTypes = decodeContentType | ||
<$> fromMaybe [] (M.lookup targetText $ configRawMediaEndpoints conf) | ||
defaultContentTypes = (decodeContentType | ||
<$> fromMaybe [] (M.lookup "any" $ configRawMediaEndpoints conf)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@steve-chavez I was wondering if we should take an "any" approach, where certain mime types are added to all requests, or an "otherwise" approach instead, which would apply the mime types only if no other setting matches the target. What do you think is best?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Dansvidania Oh, sorry I'm a bit lost here. Could you provide a sample config with the any
?
I'm thinking you mean something like:
raw-media-endpoints {
any = "image/png"
}
But not sure. A config with the otherwise
would also help me understand the idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The intent is to give the possibility to define a catch-all clause in the config so that a default mime type can be made a raw media type for all endpoints via a special "keyword" (here I refer to any
or otherwise
but they could be something else)
The difference between the two approaches would be the effect for endpoints that have a specific definition in raw-media-endpoints
(in the examples, endpoint images
has a specific definition of raw media types)
With this config requests would accept both any
types, and the types for their specific endpoint:
raw-media-endpoints {
any = "mime/foo"
images = "mime/bar"
}
a request to /images
would accept both mime/foo
and mime/bar
as raw media types.
instead, given this config
raw-media-endpoints {
images = "mime/foo"
otherwise = "mime/bar"
}
a request to /images
would accept image/jpg
only.
What I am asking is whether you think this functionality is needed, and also whether such defaults should work in additive fashion (with any
) or in alternative fashion (with otherwise
). Or maybe if both special keywords should be included?
I hope I did a better job of explaining what I mean.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Dansvidania Ah, I understand the any/otherwise
proposal now. One problem I see is that they could make all the other responses fail in a browser, same as raw-media-types
. Say I have an example html page like the one I mention here, plus another one that I'd have in /rpc/admin.html
. I could do this with the config:
raw-media-endpoints {
any = "text/html"
}
Calling the html endpoints would work but all the others would result in the same issue as #1462 (text/html requested but more than one column was selected
).
It'd be better to ask the user to be specific and instead do:
raw-media-endpoints {
/rpc/index.html = "text/html"
/rpc/admin.html = "text/html"
}
In general, I think these endpoints(html or image) will be few. So I'd go for no any/otherwise
for now. If there's a need for a more generic config we could add that later. What do you think?
Hey @dansvidania. Good to have you back!
We have been doing that on What kind of errors would you expect? An invalid media type? |
Thanks. I am looking forward to finally making some contributions.
I was mostly thinking about a warning message to state that |
@Dansvidania Good idea. That would make migration to the new version easier. Also, I have an alternative idea for the implementation, I think it could offer better UX. It would not need Here's the main idea. Before we couldn't override the response media type through postgrest/test/Feature/PgVersion96Spec.hs Lines 116 to 122 in 24064f8
postgrest/test/fixtures/schema.sql Lines 1693 to 1695 in 24064f8
Now, what's missing is the ability to And actually, the overridden header is already obtainable in headers(you could check that with a What needs to be modified is the responseContentTypeOrError check. That one should happen after obtaining the overridden header. The check logic would mostly be the same, but it should consider that header . After that, if the request postgrest/src/PostgREST/App.hs Lines 158 to 160 in 24064f8
|
@Dansvidania Let me know what you think. If you'd like to try the approach above, starting with support for |
@steve-chavez If I understand correctly the alternative you propose is for users to define the If that is the case, I am not able to see the advantage in doing it like that; it seems like it would be a bit more involved to configure (and harder to maintain?). Could you help me understand what gives better UX? Anyway, I added what I think is the implementation you suggested to the PR. Maybe we could offer both solutions at the same time? |
@Dansvidania I'm sorry. I wanted to say that for RPC it has better UX. Because you can edit the create function images(id int) returns bytea as $$
begin
perform set_config('response.headers', '[{"Content-Type": "image/jpeg"]', true);
return select files.blob from files where files.id = file.id;
end $$ language plpgsql; Then you'd be able to: GET /rpc/images?id=1
Accept: image/jpeg
Content-Type: image/jpeg
<..jpeg..> The function would accept and respond with the |
I think I get it now, thanks. It does seem convenient to define the I think this implementation could go side by side with What do you think is best? |
Oh, I was just about to leave a comment about UX. I'll still leave it here in case it brings more clarity.
Sure. Besides #1462, in #1422, the use case is overriding the media type through RPC, which didn't work. I think that shows that overriding the media type(and making pgrst As you noticed though, the example of
I believe most users(also myself) will be suited by RPC. I agree that using So it'd be better to have a single way to handle binary output for now. If more convenience is needed later, we could see about Btw, I'll mostly be working on |
@steve-chavez I have had a chance today to look in more depth at how to implement your solution. I found no way to know the overridden Content-Type(s) before invoking the RPC ( It makes a lot of sense that such a check would happen before anything is executed against the DB: given that we know the I imagine I either misunderstood something, or this is probably not the way to go. |
Oh, an additional trip to the DB wouldn't be needed. We're already getting the postgrest/src/PostgREST/Statements.hs Lines 138 to 146 in 43d71e9
postgrest/src/PostgREST/Private/QueryFragment.hs Lines 202 to 206 in 43d71e9
I believe the overriden |
@steve-chavez sorry i did not mean an additional trip. I do see that there would be only a single SQL execution. What I mean is that, in order to see those overridden headers we need to execute the SQL (at least from what I see here) Only then we are able to compare compare I believe the fact that At this point, I am wondering if i isn't just better to deprecate |
As an alternative, unless the intent was to allow dynamically defining the request In this way, they would be accessible before the request-specific SQL is executed, and still users would be able to define them in the body of RPCs. What I mean is the following, for example. create function images(id int) returns bytea as $$
begin
perform set_config('response.headers', '[{"Content-Type": "image/jpeg"]', true);
return select files.blob from files where files.id = file.id;
end $$ language plpgsql; we could search invocations of set_config, parse them and add them to the ProcDescription type. so the ProcDescription could look like this data ProcDescription = ProcDescription {
pdSchema :: Schema
, pdName :: Text
, pdDescription :: Maybe Text
, pdArgs :: [PgArg]
, pdReturnType :: RetType
, pdVolatility :: ProcVolatility
-- for example Map Text [(Text,Text)] -> [("response.headers", [("Content-Type", "image/jpg")]]
, pdOverriddenConfig :: OverriddenConfigMap
} deriving (Show, Eq) This way, the What do you think? |
@Dansvidania Sorry for the late reply, I'll try to address your concerns.
Doing a
The same reasoning could be applied for checking the headers override(#1435) or the status override(#1541). We do that logic for nothing in most of the cases(most requests don't override), but thanks to that we're able to offer more flexible HTTP logic.
Yes, the Content-Type is not really dynamic, but the solution of scanning the body of the functions could heavily impact Also that approach would be much more complex to implement than the lookup above.
All of the HTTP logic features we support can also be done with Nginx(or Caddy, etc). The point is making them more convenient to use them with just PostgREST. In general though, I agree there's a cost(like letting go the content negotiation check before executing SQL) in adding these kind of features. If the change is too complex, then of course Nginx is the answer. But if it's simple implementation-wise, we can have them and support interesting use cases(like the html rendering, or serving imgs). |
There's an alternative approach in #1548. |
@Dansvidania Check #1548 (comment)! We can keep doing the static check for content negotiation. |
@steve-chavez i am trying to catch up with the developments that happened across different issues regarding this change. Static checking seems preferable to me so that's definitely good news, but at a first glance I did not understand what is the strategy in #1548 |
@Dansvidania I'm copying the relevant parts (adjusted with the correct variable name we agreed on a couple of comments below that part) to give you the gist of it for RPC:
CREATE FUNCTION my_rpc_with_custom_accept_header (...) RETURNS bytea AS $$
[...]
$$ LANGUAGE SQL SET pgrst.accept = 'application/msgpack';
So, I think this boils down to the following:
@steve-chavez Am I missing anything? |
@wolfgangwalther I think we could omit looking at the return type. Right now our binary output and plain text output work by using the string_agg function - which precisely works for
I think it'd be simpler for the codebase to treat
The
It does accept What do you think? |
I think the I would be completely fine with throwing that error now and improving it later. However, I guess the "requirement to look at the return type" was just a consequence of my thoughts in the next paragraph (see below), because applying this logic means that the decision whether to use the
I'm not sure whether the previous logic can be re-used. Previously this was not specific per function, right? So it was just allow or reject - and then see what happens. Also - hypothetically - I might want to create a function that does return
Implementing it in steps is great, when the basic stuff allows to build on for enhancements. So if we only implement the specific case right now, thats fine with me. Would still need to iterate over that array for overloaded functions, I guess - so it would be "first match wins" right now? Or last one - whatever is easier to implement. |
If it is the return type that is looked at, it should probably be |
True, though the proc is already available as a parameter. Should be straightforward to include it: postgrest/src/PostgREST/App.hs Line 123 in 8e4687f
It won't throw an error, a bytea type will get converted to text when wrapped with
Yeah, but I think we should clear the task at hand first :). And actually If you want to return a custom json from a function, the default |
I know it does not throw an error right now. But that's strange. Returning that image Short: A RPC that serves a custom |
The
Yeah, that could be done. The extra media types approach from now doesn't preclude us from allowing to override the default response later. It's just the easiest implementation. |
Maybe I'm missing something though. @Dansvidania What do you think it would be the simplest implementation? |
Passing
Even if we only allow a single type for The more I think about it, the more I think the current implementation of allowing
I think that's the bottom line of our differing opinions - I was/am not too concerned about finding the easiest implementation. Mostly because I hope to be able to just provide ideas, without implementing myself, here. Haha :) The easiest implementation, that would still be consistent, could be:
I think this should still allow all the many next steps I suggested above, while keeping backwards compatibility. |
@wolfgangwalther You're right. Setting the
If instead of the "extra" we go with the "override" approach you suggested before, this might not be a big issue. Setting Users might still override
Thinking about the easiest implementation made me get things wrong. Your ideas were very elucidating, keep them coming :) |
@wolfgangwalther @Dansvidania I'm thinking of doing a release this week. Mostly because I want to focus next release on PostGIS features(want to merge #1564 but not have it in this release). @Dansvidania Let me know if I can help you with this one. I would like to include it in this release. |
Sorry I will only be able to work on this after the 17th.. I will try to get to it asap. Your discussion is a bit hard to follow for me, so a couple of test cases to guide the implementation would really help me get going. |
@Dansvidania I did an experiment on this branch to make sure of the implementation. So far the single media type case is working good. It's better than the What's missing is the multiple media type case, e.g. if the client requests |
apologies for being unable to work on this so far. I can see the functionality is being implemented in #1582 , I will close this PR. |
#1462 - still needs a test suite and config validation. I would appreciate some pointers as to where it is best to do the latter.