chore(server): add experiment feature flag to disable Postgres arrays in introspection

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/9804
GitOrigin-RevId: eeb378153df070ffedcada6a91d3117a0a83dde9
This commit is contained in:
Daniel Harvey 2023-07-10 12:15:35 +01:00 committed by hasura-bot
parent e4ebbbccd7
commit 213f64c061
8 changed files with 394 additions and 101 deletions

View File

@ -512,14 +512,14 @@ Postgres.
List of experimental features to be enabled.
| | |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Flag** | `--experimental-features <FEATURES>` |
| **Env var** | `HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` |
| **Accepted values** | String (Comma-separated list) |
| **Options** | `inherited_roles`, `optimise_permission_filters`, `naming_convention`, `streaming_subscriptions`, `apollo_federation`, `hide_update_many_fields`, `bigquery_string_numeric_input`, `hide_aggregation_predicates`, `hide_stream_fields` |
| **Default** | `null` |
| **Supported in** | CE, Enterprise Edition, Cloud |
| | |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Flag** | `--experimental-features <FEATURES>` |
| **Env var** | `HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` |
| **Accepted values** | String (Comma-separated list) |
| **Options** | `inherited_roles`, `optimise_permission_filters`, `naming_convention`, `streaming_subscriptions`, `apollo_federation`, `hide_update_many_fields`, `bigquery_string_numeric_input`, `hide_aggregation_predicates`, `hide_stream_fields`, `disable_postgres_arrays` |
| **Default** | `null` |
| **Supported in** | CE, Enterprise Edition, Cloud |
### Graceful Shutdown Timeout

View File

@ -15,7 +15,7 @@ import Harness.Quoter.Yaml (interpolateYaml, yaml)
import Harness.Schema qualified as Schema
import Harness.Test.Fixture qualified as Fixture
import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
import Harness.Yaml (shouldReturnYaml)
import Harness.Yaml (shouldBeYaml, shouldReturnYaml)
import Hasura.Prelude
import Test.Hspec (SpecWith, describe, it)
@ -23,102 +23,89 @@ spec :: SpecWith GlobalTestEnvironment
spec = do
Fixture.run
( NE.fromList
[ (Fixture.fixture $ Fixture.Backend Postgres.backendTypeMetadata)
{ Fixture.setupTeardown = \(testEnvironment, _) ->
[ Fixture.SetupAction
{ Fixture.setupAction =
Postgres.run_ testEnvironment setup,
Fixture.teardownAction = \_ ->
pure ()
},
Postgres.setupTablesAction schema testEnvironment
]
},
(Fixture.fixture $ Fixture.Backend Citus.backendTypeMetadata)
{ Fixture.setupTeardown = \(testEnvironment, _) ->
[ Fixture.SetupAction
{ Fixture.setupAction =
Citus.run_ testEnvironment setup,
Fixture.teardownAction = \_ ->
pure ()
},
Citus.setupTablesAction schema testEnvironment
]
},
(Fixture.fixture $ Fixture.Backend Cockroach.backendTypeMetadata)
{ Fixture.setupTeardown = \(testEnvironment, _) ->
[ Fixture.SetupAction
{ Fixture.setupAction =
Cockroach.run_ testEnvironment setup,
Fixture.teardownAction = \_ ->
pure ()
},
Cockroach.setupTablesAction schema testEnvironment
]
}
[ postgresFixture,
citusFixture,
cockroachFixture
]
)
singleArrayTests
-- run these tests with Arrays switched off
Fixture.hgeWithEnv [("HASURA_GRAPHQL_EXPERIMENTAL_FEATURES", "disable_postgres_arrays")]
$ Fixture.run
( NE.fromList
[postgresFixture, citusFixture]
)
$ do
singleArrayTests -- we check these still work with the flag on
introspectionWithDisabledFeatureTests
-- CockroachDB introspection looks different, let's not do it
Fixture.run
( NE.fromList
[postgresFixture, citusFixture]
)
introspectionTests
-- CockroachDB does not support nested arrays
-- https://www.cockroachlabs.com/docs/stable/array.html
Fixture.run
( NE.fromList
[ (Fixture.fixture $ Fixture.Backend Postgres.backendTypeMetadata)
{ Fixture.setupTeardown = \(testEnvironment, _) ->
[ Fixture.SetupAction
{ Fixture.setupAction =
Postgres.run_ testEnvironment setup,
Fixture.teardownAction = \_ ->
pure ()
},
Postgres.setupTablesAction schema testEnvironment
]
},
(Fixture.fixture $ Fixture.Backend Citus.backendTypeMetadata)
{ Fixture.setupTeardown = \(testEnvironment, _) ->
[ Fixture.SetupAction
{ Fixture.setupAction =
Citus.run_ testEnvironment setup,
Fixture.teardownAction = \_ ->
pure ()
},
Citus.setupTablesAction schema testEnvironment
]
}
]
[postgresFixture, citusFixture]
)
nestedArrayTests
-- CockroachDB does not support json arrays
Fixture.run
( NE.fromList
[ (Fixture.fixture $ Fixture.Backend Postgres.backendTypeMetadata)
{ Fixture.setupTeardown = \(testEnvironment, _) ->
[ Fixture.SetupAction
{ Fixture.setupAction =
Postgres.run_ testEnvironment setup,
Fixture.teardownAction = \_ -> pure ()
},
Postgres.setupTablesAction schema testEnvironment
]
},
(Fixture.fixture $ Fixture.Backend Citus.backendTypeMetadata)
{ Fixture.setupTeardown = \(testEnvironment, _) ->
[ Fixture.SetupAction
{ Fixture.setupAction =
Citus.run_ testEnvironment setup,
Fixture.teardownAction = \_ -> pure ()
},
Citus.setupTablesAction schema testEnvironment
]
}
]
[postgresFixture, citusFixture]
)
jsonArrayTests
postgresFixture :: Fixture.Fixture ()
postgresFixture =
(Fixture.fixture $ Fixture.Backend Postgres.backendTypeMetadata)
{ Fixture.setupTeardown = \(testEnvironment, _) ->
[ Fixture.SetupAction
{ Fixture.setupAction =
Postgres.run_ testEnvironment setup,
Fixture.teardownAction = \_ ->
pure ()
},
Postgres.setupTablesAction schema testEnvironment
]
}
citusFixture :: Fixture.Fixture ()
citusFixture =
(Fixture.fixture $ Fixture.Backend Citus.backendTypeMetadata)
{ Fixture.setupTeardown = \(testEnvironment, _) ->
[ Fixture.SetupAction
{ Fixture.setupAction =
Citus.run_ testEnvironment setup,
Fixture.teardownAction = \_ ->
pure ()
},
Citus.setupTablesAction schema testEnvironment
]
}
cockroachFixture :: Fixture.Fixture ()
cockroachFixture =
(Fixture.fixture $ Fixture.Backend Cockroach.backendTypeMetadata)
{ Fixture.setupTeardown = \(testEnvironment, _) ->
[ Fixture.SetupAction
{ Fixture.setupAction =
Cockroach.run_ testEnvironment setup,
Fixture.teardownAction = \_ ->
pure ()
},
Cockroach.setupTablesAction schema testEnvironment
]
}
setup :: Text
setup = "create type \"made_up_type\" as enum ('a', 'b', 'c');"
setup = "create type \"_made_up_type\" as enum ('a', 'b', 'c');"
--------------------------------------------------------------------------------
-- Schema
@ -154,9 +141,9 @@ enumArrayType :: Schema.ScalarType
enumArrayType =
Schema.TCustomType
$ Schema.defaultBackendScalarType
{ Schema.bstPostgres = Just "made_up_type[]",
Schema.bstCitus = Just "made_up_type[]",
Schema.bstCockroach = Just "made_up_type[]"
{ Schema.bstPostgres = Just "_made_up_type[]",
Schema.bstCitus = Just "_made_up_type[]",
Schema.bstCockroach = Just "_made_up_type[]"
}
schema :: [Schema.Table]
@ -530,3 +517,279 @@ nestedArrayTests = do
|]
shouldReturnYaml testEnvironment actual expected
introspectionTests :: SpecWith TestEnvironment
introspectionTests = do
describe "Array types become GraphQL arrays via introspection" $ do
it "Produces GraphQL arrays" $ \testEnvironment -> do
let queryTypesIntrospection :: Value
queryTypesIntrospection =
[graphql|
{
__type(name:"hasura_author") {
kind
name
fields {
name
type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
|]
expected =
[yaml|
{
"data": {
"__type": {
"name": "hasura_author",
"kind": "OBJECT",
"fields": [
{
"name": "emails",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
}
}
}
}
},
{
"name": "grid",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
}
}
}
}
},
{
"name": "id",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
}
}
},
{
"name": "jsons",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "json",
}
}
}
}
},
{
"name": "made_up",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "_made_up_type",
}
}
}
}
},
{
"name": "name",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
}
]
}
}
}
|]
actual <- postGraphql testEnvironment queryTypesIntrospection
actual `shouldBeYaml` expected
introspectionWithDisabledFeatureTests :: SpecWith TestEnvironment
introspectionWithDisabledFeatureTests = do
describe "Array types remain unknown types via introspection" $ do
it "Produces unknown types" $ \testEnvironment -> do
let queryTypesIntrospection :: Value
queryTypesIntrospection =
[graphql|
{
__type(name:"hasura_author") {
kind
name
fields {
name
type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
|]
expected =
[yaml|
{
"data": {
"__type": {
"name": "hasura_author",
"kind": "OBJECT",
"fields": [
{
"name": "emails",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "_text",
"ofType": null
}
}
},
{
"name": "grid",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "_integer",
"ofType": null
}
}
},
{
"name": "id",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
}
}
},
{
"name": "jsons",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "_json",
"ofType": null
}
}
},
{
"name": "made_up",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "__made_up_type",
"ofType": null
}
}
},
{
"name": "name",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
}
]
}
}
}
|]
actual <- postGraphql testEnvironment queryTypesIntrospection
actual `shouldBeYaml` expected

View File

@ -437,13 +437,26 @@ columnParser columnType nullability = case columnType of
-- TODO: introduce new dedicated scalars for Postgres column types.
(name, schemaType) <- case scalarType of
PGArray innerScalar -> do
name <- mkScalarTypeName innerScalar
pure
( name,
P.TList
P.NonNullable
(P.TNamed P.NonNullable $ P.Definition name Nothing Nothing [] P.TIScalar)
)
-- postgres arrays break introspection for some clients so allow them
-- to disable it
disableArrays <- retrieve Options.soPostgresArrays
case disableArrays of
Options.UsePostgresArrays -> do
name <- mkScalarTypeName innerScalar
pure
( name,
P.TList
P.NonNullable
(P.TNamed P.NonNullable $ P.Definition name Nothing Nothing [] P.TIScalar)
)
Options.DontUsePostgresArrays -> do
-- previously, we represented text[] as `_text` - recover this
-- naming:
arrayName <- mkScalarTypeName (PGUnknown $ "_" <> pgScalarTypeToText innerScalar)
pure
( arrayName,
P.TNamed P.NonNullable $ P.Definition arrayName Nothing Nothing [] P.TIScalar
)
_ -> do
name <- mkScalarTypeName scalarType
pure (name, P.TNamed P.NonNullable $ P.Definition name Nothing Nothing [] P.TIScalar)

View File

@ -268,7 +268,11 @@ buildSchemaOptions
soIncludeGroupByAggregateFields =
if EFGroupByAggregations `Set.member` expFeatures
then Options.IncludeGroupByAggregateFields
else Options.ExcludeGroupByAggregateFields
else Options.ExcludeGroupByAggregateFields,
soPostgresArrays =
if EFDisablePostgresArrays `Set.member` expFeatures
then Options.DontUsePostgresArrays
else Options.UsePostgresArrays
}
-- | Build the @QueryHasura@ context for a given role.

View File

@ -12,6 +12,7 @@ module Hasura.RQL.Types.Schema.Options
IncludeUpdateManyFields (..),
BigQueryStringNumericInput (..),
IncludeGroupByAggregateFields (..),
UsePostgresArrays (..),
)
where
@ -29,7 +30,8 @@ data SchemaOptions = SchemaOptions
soIncludeAggregationPredicates :: IncludeAggregationPredicates,
soIncludeStreamFields :: IncludeStreamFields,
soBigQueryStringNumericInput :: BigQueryStringNumericInput,
soIncludeGroupByAggregateFields :: IncludeGroupByAggregateFields
soIncludeGroupByAggregateFields :: IncludeGroupByAggregateFields,
soPostgresArrays :: UsePostgresArrays
}
-- | Should we represent numbers in our responses as numbers, or strings?
@ -144,3 +146,11 @@ data IncludeGroupByAggregateFields
= IncludeGroupByAggregateFields
| ExcludeGroupByAggregateFields
deriving (Eq, Show)
-- | if we use Postgres arrays then an array of `text` becomes `[String!]`,
-- however we want users to have the option to make it output `_text` like it
-- did before for compatibility.
data UsePostgresArrays
= UsePostgresArrays
| DontUsePostgresArrays
deriving (Eq, Show)

View File

@ -87,6 +87,7 @@ data ExperimentalFeature
| EFHideAggregationPredicates
| EFHideStreamFields
| EFGroupByAggregations
| EFDisablePostgresArrays
deriving (Bounded, Enum, Eq, Generic, Show)
experimentalFeatureKey :: ExperimentalFeature -> Text
@ -101,6 +102,7 @@ experimentalFeatureKey = \case
EFHideAggregationPredicates -> "hide_aggregation_predicates"
EFHideStreamFields -> "hide_stream_fields"
EFGroupByAggregations -> "group_by_aggregations"
EFDisablePostgresArrays -> "disable_postgres_arrays"
instance Hashable ExperimentalFeature

View File

@ -11,7 +11,7 @@ import Prelude
spec :: Spec
spec =
describe "parse array scalar types" $ do
describe "parse array scalar types with arrays switched on" $ do
let expectations =
[ ("text", PGText),
("text[]", PGArray PGText),

View File

@ -70,7 +70,8 @@ defaultSchemaOptions =
soIncludeAggregationPredicates = Options.IncludeAggregationPredicates,
soIncludeStreamFields = Options.IncludeStreamFields,
soBigQueryStringNumericInput = Options.EnableBigQueryStringNumericInput,
soIncludeGroupByAggregateFields = Options.IncludeGroupByAggregateFields
soIncludeGroupByAggregateFields = Options.IncludeGroupByAggregateFields,
soPostgresArrays = Options.UsePostgresArrays
}
instance Has NamingCase SchemaEnvironment where