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

feat: add timezone in Prefer header #3024

Merged
merged 8 commits into from
Nov 13, 2023

Conversation

taimoorzaeem
Copy link
Collaborator

@taimoorzaeem taimoorzaeem commented Oct 24, 2023

This should fix #2799.

@taimoorzaeem taimoorzaeem marked this pull request as draft October 24, 2023 13:29
@taimoorzaeem
Copy link
Collaborator Author

    context "check behaviour of Prefer: timezone=America/Los_Angeles" $
          it "should change timezone" $
            request methodGet "/timezone_values?select=*"
            [("Prefer", "handling=strict, timezone=America/Los_Angeles")]
            ""
            `shouldRespondWith`
            [json|[{"t":"2023-10-18T05:37:59.611-07:00"}, {"t":"2023-10-18T07:37:59.611-07:00"}, {"t":"2023-10-18T09:37:59.611-07:00"}]|]
            { matchStatus = 200
            , matchHeaders = [matchContentTypeJson] }

Strangely, this test is failing. But, if I run this test locally with curl it works fine.

$ postgrest-with-postgresql-15 postgrest-run
$ curl 'localhost:3000/timezone_values' -H "Prefer: handling=strict, timezone=America/Los_Angeles"

[{"t":"2023-10-18T05:37:59.611-07:00"}, 
 {"t":"2023-10-18T07:37:59.611-07:00"}, 
 {"t":"2023-10-18T09:37:59.611-07:00"}]

$ curl 'localhost:3000/timezone_values' -H "Prefer: handling=strict, timezone=America/New_York"

[{"t":"2023-10-18T08:37:59.611-04:00"}, 
 {"t":"2023-10-18T10:37:59.611-04:00"}, 
 {"t":"2023-10-18T12:37:59.611-04:00"}]

@steve-chavez @laurenceisla Is there something missing in the spec tests? Should I move these to io-tests.

@steve-chavez
Copy link
Member

steve-chavez commented Oct 30, 2023

@steve-chavez @laurenceisla Is there something missing in the spec tests?

@taimoorzaeem Yes, the spec tests don't load the in-db config. This is done with AppState.reReadConfig, and only runs for the CLI.

To make it work, the resulting AppConfig from reReadConfig result would need to be cached and then used inside app config...

-- cached schema cache so most tests run fast
baseSchemaCache <- loadSchemaCache pool testCfg
let
-- For tests that run with the same refSchemaCache
app config = do
appState <- AppState.initWithPool pool config
AppState.putPgVersion appState actualPgVersion
AppState.putSchemaCache appState (Just baseSchemaCache)
return ((), postgrest config appState $ pure ())

But that might require some refactoring. Could be done in other PR.

Should I move these to io-tests.

That's another option too. It would be the simplest one for now.

@taimoorzaeem taimoorzaeem marked this pull request as ready for review October 31, 2023 14:10
@taimoorzaeem
Copy link
Collaborator Author

@steve-chavez Hmm, the io-tests are running fine locally however, on CI they are failing. Strange!

@taimoorzaeem
Copy link
Collaborator Author

@steve-chavez @laurenceisla

def test_prefer_timezone_with_invalid_timezone(defaultenv):
    "timezone=America/XXX should set timezone to default timezone"

    env = {**defaultenv, "PGRST_DB_CONFIG": "true", "PGRST_JWT_SECRET": SECRET}

    headers = {
        "Prefer": "handling=strict, timezone=America/XXX",
    }

    with run(env=env) as postgrest:
        response = postgrest.session.get("/timezone_values", headers=headers)
        response_body = '[{"t":"2023-10-18T17:37:59.611+05:00"}, \n {"t":"2023-10-18T19:37:59.611+05:00"}, \n {"t":"2023-10-18T21:37:59.611+05:00"}]'
        assert response.text == response_body

This test sets the timezone to default timezone for invalid timezones. On my system, we have PKT timezone GMT+05 and so the tests are working fine locally. However, on github CI the default timezone is +00:00 apparently. The local timezone is different for every developer here and so this would cause the io-tests to fail locally.
How should I write this test case so it matches the response body irrespective of timezone?

@laurenceisla
Copy link
Member

laurenceisla commented Oct 31, 2023

@taimoorzaeem

How should I write this test case so it matches the response body irrespective of timezone?

Edited: According to the docs, the environment variable used to set a db timezone is TZ . So we should fix the creation script instead:

PGTZ=UTC initdb --no-locale --encoding=UTF8 --nosync -U "${superuserRole}" --auth=trust \

Replace PGTZ=UTC with TZ=$PGTZ.

Looks like it was setting the default time zone of the host, with this change that should be fixed.

@taimoorzaeem taimoorzaeem force-pushed the prefer/timezone branch 2 times, most recently from 79c0822 to c9db1eb Compare November 3, 2023 17:48
src/PostgREST/ApiRequest/Preferences.hs Outdated Show resolved Hide resolved
src/PostgREST/ApiRequest/Preferences.hs Outdated Show resolved Hide resolved
src/PostgREST/Query.hs Outdated Show resolved Hide resolved
src/PostgREST/ApiRequest/Preferences.hs Outdated Show resolved Hide resolved
@laurenceisla
Copy link
Member

laurenceisla commented Nov 4, 2023

Looking good. Just those refactors and it should be ready to merge.

BTW. Do you know why is it giving those IO errors?

@taimoorzaeem
Copy link
Collaborator Author

taimoorzaeem commented Nov 4, 2023

BTW. Do you know why is it giving those IO errors?

Nope, I don't know why IO-tests are failing on some pg versions.

test/io/postgrest.py Show resolved Hide resolved
@laurenceisla laurenceisla force-pushed the prefer/timezone branch 2 times, most recently from 7879124 to b626142 Compare November 6, 2023 22:29
test/io/test_io.py Outdated Show resolved Hide resolved
test/io/fixtures.sql Outdated Show resolved Hide resolved
@taimoorzaeem
Copy link
Collaborator Author

@taimoorzaeem Just tried this and it doesn't seem correct according to Prefer: handling=strict semantics, we should respond with an error in this case.

@steve-chavez Good catch! The only thing left here I guess is that these tests don't belong to io-tests. We should move them to spec-tests but that needs refactoring as you stated earlier which could be done in another PR.

@laurenceisla
Copy link
Member

laurenceisla commented Nov 9, 2023

The only thing left here I guess is that these tests don't belong to io-tests. We should move them to spec-tests but that needs refactoring as you stated earlier which could be done in another PR.

I'm thinking about this, can the result of the queryTimezones be handled by the Schema Cache, instead of using the App Config as it is now? Or is there a reason against doing so? It could be alongside the queries in:

querySchemaCache :: AppConfig -> SQL.Transaction SchemaCache
querySchemaCache AppConfig{..} = do

If I'm not mistaken, the Spec Tests should work OK then.

@steve-chavez
Copy link
Member

steve-chavez commented Nov 9, 2023

I'm thinking about this, can the result of the queryTimezones be handled by the Schema Cache, instead of using the App Config as it is now? Or is there a reason against doing so?

@laurenceisla You're right. So the only reason to use the in-db config for db settings is to not expose them in the OpenAPI output (which uses the scheme cache). Some things like the roleSettings we cache in AppConfig should definitely not be exposed but the available timezones should.


@taimoorzaeem One more thing, the Preference-Applied header is not including the timezone currently:

curl localhost:3000/projects -H "Prefer: handling=strict, timezone=Europe/Berlin" -i
HTTP/1.1 200 OK
Preference-Applied: handling=strict

This might be easier to test once you move the io tests to spec tests.

@steve-chavez
Copy link
Member

@taimoorzaeem Also a note. When moving the timezones to the Schema Cache, you can just leave the toJSON output as empty for now. Something like:

instance JSON.ToJSON SchemaCache where
  toJSON (SchemaCache tabs rels routs reps _ _) = JSON.object [
      "dbTables"          .= JSON.toJSON tabs
    , "dbRelationships"   .= JSON.toJSON rels
    , "dbRoutines"        .= JSON.toJSON routs
    , "dbRepresentations" .= JSON.toJSON reps
    , "dbMediaHandlers"   .= JSON.emptyArray
    , "dbTimezones"       .= JSON.emptyArray
    ]

We're working on a pure SQL generator for OpenAPI on https://github.com/PostgREST/postgrest-openapi/. So it's not worth the effort to do it on the Haskell side for now.

Comment on lines 154 to 156
hasTimezone p = BS.take 9 p == "timezone="
getTimezoneFromPrefs = listToMaybe [ PreferTimezone (BS.drop 9 p) | p <- prefs, hasTimezone p && S.member (BS.drop 9 p) acceptedTzNames]
checkPrefs p = p `notElem` acceptedPrefs && BS.drop 9 p `S.notMember` acceptedTzNames
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will DRY this a bit. I think the BS.drop 9/BS.take 9 can be removed.

Comment on lines +117 to +118
postJsonArrayTest "1000" "/perf_articles?columns=id,body" "15M"
postJsonArrayTest "10000" "/perf_articles?columns=id,body" "15M"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get this locally too. I think the increased memory is because of the cached timezones (~1K).

Copy link
Member

@steve-chavez steve-chavez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@taimoorzaeem Great work! 💯

@steve-chavez steve-chavez merged commit 3c1a7f2 into PostgREST:main Nov 13, 2023
@taimoorzaeem taimoorzaeem deleted the prefer/timezone branch November 14, 2023 16:06
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.

Prefer: timezone header
3 participants