feat: Make naming-convention feature generally available

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/10214
GitOrigin-RevId: ad99b6e4ad6dbab2b0c6fa7a09b48dcd4d06ee1f
This commit is contained in:
Philip Lykke Carlsen 2023-08-31 11:22:24 +02:00 committed by hasura-bot
parent 1ffdff66a9
commit 76936f0972
11 changed files with 13 additions and 239 deletions

View File

@ -530,7 +530,7 @@ 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`, `optimize_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` |
| **Options** | `inherited_roles`, `optimize_permission_filters`, `naming_convention` (deprecated), `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 |

View File

@ -125,20 +125,7 @@ understand which fields are available and how they can be used.
Not to worry. Hasura can help us simplify this by allowing us to customize the root-level fields of our sources. This
means that we can add a namespace to each database, keeping each set of fields organized.
To do this, we first need to set an environment variable for our project. In Hasura Cloud, your environment variables
are controlled via the project's settings. You can reach these by clicking on the name of your project in the top-right
corner of the Console.
Then, click on the `Env vars` in the project's sidebar and `+ New Env Var` before entering
`HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` and setting its value to `naming_convention`:
<Thumbnail
src="/img/resources/use-case/access-layer/add-env-var.png"
alt="Adding an environment variable to Hasura"
width="1000px"
/>
Finally, head back to the `Data` tab of the Console and choose `Data Manager` in the side bar. Click `Edit` for our
To do this, head to the `Data` tab of the Console and choose `Data Manager` in the side bar. Click `Edit` for our
`Banking` source:
<Thumbnail

View File

@ -24,13 +24,6 @@ import GraphiQLIDE from '@site/src/components/GraphiQLIDE';
The Hasura GraphQL Engine generates names for various schema objects (fields, types, arguments, etc.) and the naming
convention for these autogenerated names can be customized to suit your needs.
:::tip Note
Naming conventions are an experimental feature and can be enabled by adding `naming_convention` to the
`HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` environment variable array or with the server flag `--experimental-feature`.
:::
:::note Supported from
Naming conventions are available at version `v2.8.0` and higher.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

View File

@ -1,34 +1,11 @@
module Hasura.GraphQL.Schema.NamingCase
( isGraphqlCase,
hasNamingConventionChanged,
)
where
import Data.HashSet qualified as Set
import Hasura.Prelude
import Hasura.RQL.Types.NamingCase
import Hasura.Server.Types (ExperimentalFeature (..))
isGraphqlCase :: NamingCase -> Bool
isGraphqlCase GraphqlCase = True
isGraphqlCase _ = False
-- | Check if naming convention has changed
-- The value of naming convention depends on whether the naming convention is enabled
-- in experimental features and what the default naming convention
-- (`HASURA_GRAPHQL_DEFAULT_NAMING_CONVENTION`) is hence use both these values to
-- decide if naming convention has changed
hasNamingConventionChanged :: (Set.HashSet ExperimentalFeature, NamingCase) -> (Set.HashSet ExperimentalFeature, NamingCase) -> Bool
hasNamingConventionChanged (prevExperimentalFeatures, prevDefaultNamingCase) (currExperimentalFeatures, currDefaultNamingCase) =
case ((EFNamingConventions `elem` prevExperimentalFeatures, prevDefaultNamingCase), (EFNamingConventions `elem` currExperimentalFeatures, currDefaultNamingCase)) of
-- naming convention has been enabled, and the default naming convention is not
-- HasuraCase then the naming convention has changed
((False, _), (True, GraphqlCase)) -> True
-- naming is enabled but the default naming convention changes, then the naming
-- convention has changed
((True, GraphqlCase), (True, HasuraCase)) -> True
((True, HasuraCase), (True, GraphqlCase)) -> True
-- graphql-case naming convention has been disabled, then the naming convention
-- changes
((True, GraphqlCase), (False, _)) -> True
_ -> False

View File

@ -60,7 +60,6 @@ import Data.Word (Word16)
import GHC.AssertNF.CPP
import GHC.Int (Int64)
import Hasura.GraphQL.ParameterizedQueryHash (ParameterizedQueryHash)
import Hasura.GraphQL.Schema.NamingCase (hasNamingConventionChanged)
import Hasura.GraphQL.Transport.HTTP.Protocol
import Hasura.GraphQL.Transport.WebSocket.Protocol
import Hasura.Logging qualified as L
@ -421,7 +420,7 @@ websocketConnectionReaper getLatestConfig getSchemaCache ws@(WSServer _ userConf
hasBigqueryStringNumericInputChanged = bigqueryStringNumericInput currSqlGenCtx /= bigqueryStringNumericInput prevSqlGenCtx
hasHideAggregationPredicatesChanged = (EFHideAggregationPredicates `elem` currExperimentalFeatures) && (EFHideAggregationPredicates `elem` prevExperimentalFeatures)
hasHideStreamFieldsChanged = (EFHideStreamFields `elem` currExperimentalFeatures) && (EFHideStreamFields `elem` prevExperimentalFeatures)
hasDefaultNamingCaseChanged = hasNamingConventionChanged (prevExperimentalFeatures, prevDefaultNamingCase) (currExperimentalFeatures, currDefaultNamingCase)
hasDefaultNamingCaseChanged = prevDefaultNamingCase /= currDefaultNamingCase
if
-- if CORS policy has changed, close all connections
| hasCorsPolicyChanged ->

View File

@ -88,7 +88,6 @@ import Hasura.RQL.Types.EventTrigger
import Hasura.RQL.Types.Metadata
import Hasura.RQL.Types.Metadata.Backend
import Hasura.RQL.Types.Metadata.Object
import Hasura.RQL.Types.NamingCase
import Hasura.RQL.Types.OpenTelemetry
import Hasura.RQL.Types.QueryCollection
import Hasura.RQL.Types.Relationships.Remote
@ -865,13 +864,10 @@ buildSchemaCacheRule logger env mSchemaRegistryContext = proc (MetadataWithResou
-- not forcing the evaluation here results in a measurable negative impact
-- on memory residency as measured by our benchmark
let !defaultNC = _cdcDefaultNamingConvention dynamicConfig
!isNamingConventionEnabled = EFNamingConventions `elem` (_cdcExperimentalFeatures dynamicConfig)
!namingConv <-
bindA
-<
if isNamingConventionEnabled
then getNamingCase sourceCustomization (namingConventionSupport @b) defaultNC
else pure HasuraCase
getNamingCase sourceCustomization (namingConventionSupport @b) defaultNC
let resolvedCustomization = mkResolvedSourceCustomization sourceCustomization namingConv
-- sql functions
@ -1225,7 +1221,6 @@ buildSchemaCacheRule logger env mSchemaRegistryContext = proc (MetadataWithResou
((remoteSchemaInvalidationKeys, orderedRoles, fmap encJToLBS . siRemotes <$> storedIntrospection, _cdcSchemaSampledFeatureFlags dynamicConfig), InsOrdHashMap.elems remoteSchemas)
let remoteSchemaCtxMap = HashMap.map fst remoteSchemaMap
!defaultNC = _cdcDefaultNamingConvention dynamicConfig
!isNamingConventionEnabled = EFNamingConventions `elem` (_cdcExperimentalFeatures dynamicConfig)
let backendInvalidationKeys = Inc.selectD #_ikBackends invalidationKeys
backendCache <- resolveBackendCache -< (backendInvalidationKeys, BackendMap.elems backendConfigs)
@ -1239,7 +1234,7 @@ buildSchemaCacheRule logger env mSchemaRegistryContext = proc (MetadataWithResou
Inc.keyed
( \_ exists ->
AB.dispatchAnyBackendArrow @BackendMetadata @BackendEventTrigger
( proc (backendInfoAndSourceMetadata :: BackendInfoAndSourceMetadata b, (dynamicConfig, invalidationKeys, storedIntrospection, defaultNC, isNamingConventionEnabled)) -> do
( proc (backendInfoAndSourceMetadata :: BackendInfoAndSourceMetadata b, (dynamicConfig, invalidationKeys, storedIntrospection, defaultNC)) -> do
let sourceMetadata = _bcasmSourceMetadata backendInfoAndSourceMetadata
sourceName = _smName sourceMetadata
sourceInvalidationsKeys = Inc.selectD #_ikSources invalidationKeys
@ -1258,7 +1253,7 @@ buildSchemaCacheRule logger env mSchemaRegistryContext = proc (MetadataWithResou
let metadataInvalidationKey = Inc.selectD #_ikMetadata invalidationKeys
(tableInputs, _, _) = unzip3 $ map mkTableInputs $ InsOrdHashMap.elems $ _smTables sourceMetadata
scNamingConvention = _scNamingConvention $ _smCustomization sourceMetadata
!namingConv = if isNamingConventionEnabled then fromMaybe defaultNC scNamingConvention else HasuraCase
!namingConv = fromMaybe defaultNC scNamingConvention
tablesCoreInfo <-
buildTableCache
-<
@ -1297,7 +1292,7 @@ buildSchemaCacheRule logger env mSchemaRegistryContext = proc (MetadataWithResou
$ PartiallyResolvedSource sourceMetadata sourceConfig source tablesCoreInfo eventTriggerInfoMaps
)
-<
(exists, (dynamicConfig, invalidationKeys, storedIntrospection, defaultNC, isNamingConventionEnabled))
(exists, (dynamicConfig, invalidationKeys, storedIntrospection, defaultNC))
)
|)
(HashMap.fromList $ InsOrdHashMap.toList backendInfoAndSourceMetadata)

View File

@ -79,6 +79,8 @@ newtype InstanceId = InstanceId {getInstanceId :: Text}
generateInstanceId :: IO InstanceId
generateInstanceId = InstanceId <$> generateFingerprint
{-# DEPRECATED EFNamingConventions "The naming-convention experimental feature is now globally enabled" #-}
data ExperimentalFeature
= EFInheritedRoles
| EFOptimizePermissionFilters

View File

@ -1236,23 +1236,23 @@ mkServeOptionsSpec =
rawServeOptions = emptyServeOptionsRaw
-- When
-- When
env = [(UUT._envVar UUT.experimentalFeaturesOption, "inherited_roles,optimize_permission_filters,naming_convention")]
env = [(UUT._envVar UUT.experimentalFeaturesOption, "inherited_roles,optimize_permission_filters")]
-- Then
result = UUT.runWithEnv env (UUT.mkServeOptions @Hasura rawServeOptions)
fmap (UUT.soExperimentalFeatures) result
`Hspec.shouldBe` Right (Set.fromList [Types.EFInheritedRoles, Types.EFOptimizePermissionFilters, Types.EFNamingConventions])
`Hspec.shouldBe` Right (Set.fromList [Types.EFInheritedRoles, Types.EFOptimizePermissionFilters])
Hspec.it "Arg > Env" $ do
let -- Given
rawServeOptions = emptyServeOptionsRaw {UUT.rsoExperimentalFeatures = Just (Set.fromList [Types.EFInheritedRoles, Types.EFOptimizePermissionFilters, Types.EFNamingConventions])}
rawServeOptions = emptyServeOptionsRaw {UUT.rsoExperimentalFeatures = Just (Set.fromList [Types.EFInheritedRoles, Types.EFOptimizePermissionFilters])}
-- When
env = [(UUT._envVar UUT.experimentalFeaturesOption, "inherited_roles")]
-- Then
result = UUT.runWithEnv env (UUT.mkServeOptions @Hasura rawServeOptions)
fmap (UUT.soExperimentalFeatures) result
`Hspec.shouldBe` Right (Set.fromList [Types.EFInheritedRoles, Types.EFOptimizePermissionFilters, Types.EFNamingConventions])
`Hspec.shouldBe` Right (Set.fromList [Types.EFInheritedRoles, Types.EFOptimizePermissionFilters])
Hspec.describe "soEventsFetchBatchSize" $ do
Hspec.it "Env > Nothing" $ do

View File

@ -1,158 +0,0 @@
- description: create table 1
url: /v1/query
status: 200
response:
result_type: CommandOk
result:
query:
type: run_sql
args:
source: pg1
sql: |
create table author_local(
id serial primary key,
author_name text unique
);
INSERT INTO author_local (author_name)
VALUES ('Author 1'), ('Author 2');
- description: track table
url: /v1/metadata
status: 200
response:
message: success
query:
type: pg_track_table
args:
table: author_local
source: pg1
- description: Simple GraphQL query to fetch items from the source table
url: /v1/graphql
status: 200
response:
data:
author_local:
- id: 1
author_name: "Author 1"
__typename: author_local
- id: 2
author_name: "Author 2"
__typename: author_local
query:
query: |
query {
author_local {
id
author_name
__typename
}
}
- description: Lookup by pk
url: /v1/graphql
status: 200
response:
data:
author_local_by_pk:
id: 1
author_name: "Author 1"
__typename: author_local
query:
query: |
query {
author_local_by_pk(id: 1) {
id
author_name
__typename
}
}
- description: Aggregate
url: /v1/graphql
status: 200
response:
data:
author_local_aggregate:
__typename: author_local_aggregate
aggregate:
__typename: author_local_aggregate_fields
count: 1
query:
query: |
query MyQuery {
author_local_aggregate(where: {author_name: {_eq: "Author 2"}}) {
__typename
aggregate {
__typename
count
}
}
}
- description: Insert
url: /v1/graphql
status: 200
response:
data:
insert_author_local:
__typename: author_local_mutation_response
returning:
- __typename: author_local
id: 3
author_name: Author 3
query:
query: |
mutation MyMutation {
insert_author_local(objects: {author_name: "Author 3", id: 3}) {
__typename
returning {
__typename
id
author_name
}
}
}
- description: Delete by pk
url: /v1/graphql
status: 200
response:
data:
delete_author_local_by_pk:
__typename: author_local
id: 3
author_name: Author 3
query:
query: |
mutation MyMutation {
delete_author_local_by_pk(id: 3) {
__typename
id
author_name
}
}
- description: untrack table
url: /v1/metadata
status: 200
response:
message: success
query:
type: pg_untrack_table
args:
table: author_local
source: pg1
- description: drop table
url: /v1/query
status: 200
response:
result_type: CommandOk
result:
query:
type: run_sql
args:
source: pg1
sql: |
drop table author_local;

View File

@ -2,7 +2,6 @@ import pytest
from validate import check_query_f
@pytest.mark.hge_env('HASURA_GRAPHQL_EXPERIMENTAL_FEATURES', 'naming_convention')
class TestNamingConventions:
@classmethod
@ -19,7 +18,6 @@ class TestNamingConventions:
def test_enum_value_convention(self, hge_ctx):
check_query_f(hge_ctx, self.dir() + '/enum_value_convention.yaml')
@pytest.mark.hge_env('HASURA_GRAPHQL_EXPERIMENTAL_FEATURES', 'naming_convention')
class TestNamingConventionsTypeAndFieldNamesGraphqlDefault:
@classmethod
@ -33,7 +31,6 @@ class TestNamingConventionsTypeAndFieldNamesGraphqlDefault:
def test_type_and_field_names(self, hge_ctx):
check_query_f(hge_ctx, self.dir() + '/type_and_field_names_graphql_default.yaml')
@pytest.mark.hge_env('HASURA_GRAPHQL_EXPERIMENTAL_FEATURES', 'naming_convention')
class TestNamingConventionsTypeAndFieldNamesHasuraDefault:
@classmethod
@ -47,7 +44,6 @@ class TestNamingConventionsTypeAndFieldNamesHasuraDefault:
def test_type_and_field_names(self, hge_ctx):
check_query_f(hge_ctx, self.dir() + '/type_and_field_names_hasura_default.yaml')
@pytest.mark.hge_env('HASURA_GRAPHQL_EXPERIMENTAL_FEATURES', 'naming_convention')
class TestNamingConventionsTypeAndFieldNamesGraphqlDefaultWithPrefixAndSuffix:
@classmethod
@ -67,7 +63,6 @@ class TestNamingConventionsTypeAndFieldNamesGraphqlDefaultWithPrefixAndSuffix:
def test_type_and_field_names_with_prefix_and_suffix(self, hge_ctx):
check_query_f(hge_ctx, self.dir() + '/type_and_field_names_graphql_default_with_prefix_and_suffix.yaml')
@pytest.mark.hge_env('HASURA_GRAPHQL_EXPERIMENTAL_FEATURES', 'naming_convention')
class TestNamingConventionsTypeAndFieldNamesHasuraDefaultWithPrefixAndSuffix:
@classmethod
@ -88,7 +83,6 @@ class TestNamingConventionsTypeAndFieldNamesHasuraDefaultWithPrefixAndSuffix:
check_query_f(hge_ctx, self.dir() + '/type_and_field_names_hasura_default_with_prefix_and_suffix.yaml')
@pytest.mark.backend('mssql')
@pytest.mark.hge_env('HASURA_GRAPHQL_EXPERIMENTAL_FEATURES', 'naming_convention')
class TestNamingConventionsFailure:
@classmethod
def dir(cls):
@ -120,7 +114,6 @@ class TestNamingConventionsFailure:
"error": "sources other than postgres do not support graphql-default as naming convention yet",
}
@pytest.mark.hge_env('HASURA_GRAPHQL_EXPERIMENTAL_FEATURES', 'naming_convention')
@pytest.mark.hge_env('HASURA_GRAPHQL_DEFAULT_NAMING_CONVENTION', 'graphql-default')
class TestDefaultNamingConvention:
@ -134,17 +127,3 @@ class TestDefaultNamingConvention:
def test_default_global_naming_convention(self, hge_ctx):
check_query_f(hge_ctx, self.dir() + '/default_global_naming_convention.yaml')
@pytest.mark.hge_env('HASURA_GRAPHQL_EXPERIMENTAL_FEATURES', None) # must be unset
class TestNamingConventionWithoutExperimentalFeature:
@classmethod
def dir(cls):
return "queries/naming_conventions"
@pytest.fixture(scope='class', autouse=True)
def add_customized_source(self, add_source):
add_source('pg1', customization={'naming_convention': 'graphql-default'})
def test_naming_convention_without_feature_turned_on(self, hge_ctx):
check_query_f(hge_ctx, self.dir() + '/naming_convention_without_feature_turned_on.yaml')