mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 01:12:56 +03:00
Logical Models for BigQuery
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/8447 Co-authored-by: Daniel Harvey <4729125+danieljharvey@users.noreply.github.com> Co-authored-by: Nicolas Beaussart <7281023+beaussan@users.noreply.github.com> Co-authored-by: Antoine Leblanc <1618949+nicuveo@users.noreply.github.com> Co-authored-by: Varun Choudhary <68095256+Varun-Choudhary@users.noreply.github.com> Co-authored-by: ananya-2410 <107847554+ananya-2410@users.noreply.github.com> Co-authored-by: Matthew Goodwin <49927862+m4ttheweric@users.noreply.github.com> Co-authored-by: Abhijeet Khangarot <26903230+abhi40308@users.noreply.github.com> Co-authored-by: Puru Gupta <32328846+purugupta99@users.noreply.github.com> Co-authored-by: Gil Mizrahi <8547573+soupi@users.noreply.github.com> Co-authored-by: Rob Dominguez <24390149+robertjdominguez@users.noreply.github.com> GitOrigin-RevId: ddef9d54bfad6b7d5dc51251dbe47eac43995da3
This commit is contained in:
parent
63eabc2374
commit
b2f683f56d
@ -145,7 +145,7 @@ test-logical-models:
|
|||||||
GRAPHQL_ENGINE=$(GRAPHQL_ENGINE_PATH) \
|
GRAPHQL_ENGINE=$(GRAPHQL_ENGINE_PATH) \
|
||||||
cabal run api-tests:exe:api-tests
|
cabal run api-tests:exe:api-tests
|
||||||
HASURA_TEST_BACKEND_TYPE=BigQuery \
|
HASURA_TEST_BACKEND_TYPE=BigQuery \
|
||||||
HSPEC_MATCH=Metadata.LogicalModel \
|
HSPEC_MATCH=LogicalModel \
|
||||||
GRAPHQL_ENGINE=$(GRAPHQL_ENGINE_PATH) \
|
GRAPHQL_ENGINE=$(GRAPHQL_ENGINE_PATH) \
|
||||||
cabal run api-tests:exe:api-tests
|
cabal run api-tests:exe:api-tests
|
||||||
HASURA_TEST_BACKEND_TYPE=SQLServer \
|
HASURA_TEST_BACKEND_TYPE=SQLServer \
|
||||||
|
@ -126,6 +126,7 @@ library
|
|||||||
Test.DataConnector.MockAgent.UpdateMutationsSpec
|
Test.DataConnector.MockAgent.UpdateMutationsSpec
|
||||||
Test.DataConnector.QuerySpec
|
Test.DataConnector.QuerySpec
|
||||||
Test.DataConnector.SelectPermissionsSpec
|
Test.DataConnector.SelectPermissionsSpec
|
||||||
|
Test.Databases.BigQuery.LogicalModelsSpec
|
||||||
Test.Databases.BigQuery.Queries.SpatialTypesSpec
|
Test.Databases.BigQuery.Queries.SpatialTypesSpec
|
||||||
Test.Databases.BigQuery.Queries.TypeInterpretationSpec
|
Test.Databases.BigQuery.Queries.TypeInterpretationSpec
|
||||||
Test.Databases.BigQuery.Schema.ComputedFields.TableSpec
|
Test.Databases.BigQuery.Schema.ComputedFields.TableSpec
|
||||||
|
@ -0,0 +1,799 @@
|
|||||||
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
|
||||||
|
-- | Access to the SQL
|
||||||
|
module Test.Databases.BigQuery.LogicalModelsSpec (spec) where
|
||||||
|
|
||||||
|
import Data.Aeson (Value)
|
||||||
|
import Data.List.NonEmpty qualified as NE
|
||||||
|
import Data.String.Interpolate (i)
|
||||||
|
import Data.Time.Calendar.OrdinalDate
|
||||||
|
import Data.Time.Clock
|
||||||
|
import Harness.Backend.BigQuery qualified as BigQuery
|
||||||
|
import Harness.GraphqlEngine qualified as GraphqlEngine
|
||||||
|
import Harness.Quoter.Graphql
|
||||||
|
import Harness.Quoter.Yaml (interpolateYaml, yaml)
|
||||||
|
import Harness.Test.BackendType qualified as BackendType
|
||||||
|
import Harness.Test.Fixture qualified as Fixture
|
||||||
|
import Harness.Test.Schema (Table (..), table)
|
||||||
|
import Harness.Test.Schema qualified as Schema
|
||||||
|
import Harness.Test.SchemaName (unSchemaName)
|
||||||
|
import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment (options), getBackendTypeConfig)
|
||||||
|
import Harness.Yaml (shouldBeYaml, shouldReturnYaml)
|
||||||
|
import Hasura.Prelude
|
||||||
|
import Test.Hspec (SpecWith, describe, it)
|
||||||
|
|
||||||
|
-- ** Preamble
|
||||||
|
|
||||||
|
featureFlagForLogicalModels :: String
|
||||||
|
featureFlagForLogicalModels = "HASURA_FF_LOGICAL_MODEL_INTERFACE"
|
||||||
|
|
||||||
|
spec :: SpecWith GlobalTestEnvironment
|
||||||
|
spec =
|
||||||
|
Fixture.hgeWithEnv [(featureFlagForLogicalModels, "True")] $
|
||||||
|
Fixture.runClean -- re-run fixture setup on every test
|
||||||
|
( NE.fromList
|
||||||
|
[ (Fixture.fixture $ Fixture.Backend BigQuery.backendTypeMetadata)
|
||||||
|
{ Fixture.setupTeardown = \(testEnvironment, _) ->
|
||||||
|
[ BigQuery.setupTablesAction schema testEnvironment
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
tests
|
||||||
|
|
||||||
|
-- ** Setup and teardown
|
||||||
|
|
||||||
|
-- we add and track a table here as it's the only way we can currently define a
|
||||||
|
-- return type
|
||||||
|
schema :: [Schema.Table]
|
||||||
|
schema =
|
||||||
|
[ (table "article")
|
||||||
|
{ tableColumns =
|
||||||
|
[ Schema.column "id" Schema.TInt,
|
||||||
|
Schema.column "title" Schema.TStr,
|
||||||
|
Schema.column "content" Schema.TStr,
|
||||||
|
Schema.column "date" Schema.TUTCTime
|
||||||
|
],
|
||||||
|
tableData =
|
||||||
|
[ [ Schema.VInt 1,
|
||||||
|
Schema.VStr "Dogs",
|
||||||
|
Schema.VStr "I like to eat dog food I am a dogs I like to eat dog food I am a dogs I like to eat dog food I am a dogs",
|
||||||
|
Schema.VUTCTime (UTCTime (fromOrdinalDate 2000 1) 0)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
(table "stuff")
|
||||||
|
{ tableColumns =
|
||||||
|
[ Schema.column "thing" Schema.TInt,
|
||||||
|
Schema.column "date" Schema.TUTCTime
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
tests :: SpecWith TestEnvironment
|
||||||
|
tests = do
|
||||||
|
let query :: Text
|
||||||
|
query = "SELECT * FROM UNNEST([STRUCT('hello' as one, 'world' as two), ('welcome', 'friend')])"
|
||||||
|
|
||||||
|
helloWorldLogicalModel :: Schema.LogicalModel
|
||||||
|
helloWorldLogicalModel =
|
||||||
|
(Schema.logicalModel "hello_world_function" query)
|
||||||
|
{ Schema.logicalModelColumns =
|
||||||
|
[ Schema.logicalModelColumn "one" Schema.TStr,
|
||||||
|
Schema.logicalModelColumn "two" Schema.TStr
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
describe "Testing Logical Models" $ do
|
||||||
|
it "Descriptions and nullability appear in the schema" $ \testEnvironment -> do
|
||||||
|
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
|
||||||
|
sourceName = BackendType.backendSourceName backendTypeMetadata
|
||||||
|
|
||||||
|
nullableQuery = "SELECT thing / 2 AS divided, null as something_nullable FROM stuff"
|
||||||
|
|
||||||
|
descriptionsAndNullableLogicalModel :: Schema.LogicalModel
|
||||||
|
descriptionsAndNullableLogicalModel =
|
||||||
|
(Schema.logicalModel "divided_stuff" nullableQuery)
|
||||||
|
{ Schema.logicalModelColumns =
|
||||||
|
[ (Schema.logicalModelColumn "divided" Schema.TInt)
|
||||||
|
{ Schema.logicalModelColumnDescription = Just "A divided thing"
|
||||||
|
},
|
||||||
|
(Schema.logicalModelColumn "something_nullable" Schema.TInt)
|
||||||
|
{ Schema.logicalModelColumnDescription = Just "Something nullable",
|
||||||
|
Schema.logicalModelColumnNullable = True
|
||||||
|
}
|
||||||
|
],
|
||||||
|
Schema.logicalModelArguments =
|
||||||
|
[ Schema.logicalModelColumn "unused" Schema.TInt
|
||||||
|
],
|
||||||
|
Schema.logicalModelReturnTypeDescription = Just "Return type description"
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema.trackLogicalModel sourceName descriptionsAndNullableLogicalModel testEnvironment
|
||||||
|
|
||||||
|
let queryTypesIntrospection :: Value
|
||||||
|
queryTypesIntrospection =
|
||||||
|
[graphql|
|
||||||
|
query {
|
||||||
|
__type(name: "divided_stuff") {
|
||||||
|
name
|
||||||
|
description
|
||||||
|
fields {
|
||||||
|
name
|
||||||
|
description
|
||||||
|
type {
|
||||||
|
name
|
||||||
|
kind
|
||||||
|
ofType {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|]
|
||||||
|
|
||||||
|
expected =
|
||||||
|
[interpolateYaml|
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"__type": {
|
||||||
|
"description": "Return type description",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"description": "A divided thing",
|
||||||
|
"name": "divided",
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"name": "Int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Something nullable",
|
||||||
|
"name": "something_nullable",
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "Int",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "divided_stuff"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|]
|
||||||
|
|
||||||
|
actual <- GraphqlEngine.postGraphql testEnvironment queryTypesIntrospection
|
||||||
|
|
||||||
|
actual `shouldBeYaml` expected
|
||||||
|
|
||||||
|
it "Runs the absolute simplest query that takes no parameters" $ \testEnvironment -> do
|
||||||
|
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
|
||||||
|
source = BackendType.backendSourceName backendTypeMetadata
|
||||||
|
|
||||||
|
Schema.trackLogicalModel source helloWorldLogicalModel testEnvironment
|
||||||
|
|
||||||
|
let expected =
|
||||||
|
[yaml|
|
||||||
|
data:
|
||||||
|
hello_world_function:
|
||||||
|
- one: "hello"
|
||||||
|
two: "world"
|
||||||
|
- one: "welcome"
|
||||||
|
two: "friend"
|
||||||
|
|]
|
||||||
|
|
||||||
|
actual :: IO Value
|
||||||
|
actual =
|
||||||
|
GraphqlEngine.postGraphql
|
||||||
|
testEnvironment
|
||||||
|
[graphql|
|
||||||
|
query {
|
||||||
|
hello_world_function {
|
||||||
|
one
|
||||||
|
two
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|]
|
||||||
|
|
||||||
|
shouldReturnYaml (options testEnvironment) actual expected
|
||||||
|
|
||||||
|
it "Runs simple query with a basic where clause" $ \testEnvironment -> do
|
||||||
|
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
|
||||||
|
source = BackendType.backendSourceName backendTypeMetadata
|
||||||
|
|
||||||
|
Schema.trackLogicalModel source helloWorldLogicalModel testEnvironment
|
||||||
|
|
||||||
|
let expected =
|
||||||
|
[yaml|
|
||||||
|
data:
|
||||||
|
hello_world_function:
|
||||||
|
- one: "hello"
|
||||||
|
two: "world"
|
||||||
|
|]
|
||||||
|
|
||||||
|
actual :: IO Value
|
||||||
|
actual =
|
||||||
|
GraphqlEngine.postGraphql
|
||||||
|
testEnvironment
|
||||||
|
[graphql|
|
||||||
|
query {
|
||||||
|
hello_world_function (where: { two: { _eq: "world" } }){
|
||||||
|
one
|
||||||
|
two
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|]
|
||||||
|
|
||||||
|
shouldReturnYaml (options testEnvironment) actual expected
|
||||||
|
|
||||||
|
it "Runs a simple query using distinct_on and order_by" $ \testEnvironment -> do
|
||||||
|
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
|
||||||
|
source = BackendType.backendSourceName backendTypeMetadata
|
||||||
|
|
||||||
|
queryWithDuplicates :: Text
|
||||||
|
queryWithDuplicates = "SELECT * FROM UNNEST([STRUCT('hello' as one, 'world' as two), ('hello', 'friend')])"
|
||||||
|
|
||||||
|
helloWorldLogicalModelWithDuplicates :: Schema.LogicalModel
|
||||||
|
helloWorldLogicalModelWithDuplicates =
|
||||||
|
(Schema.logicalModel "hello_world_function" queryWithDuplicates)
|
||||||
|
{ Schema.logicalModelColumns =
|
||||||
|
[ Schema.logicalModelColumn "one" Schema.TStr,
|
||||||
|
Schema.logicalModelColumn "two" Schema.TStr
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema.trackLogicalModel source helloWorldLogicalModelWithDuplicates testEnvironment
|
||||||
|
|
||||||
|
let expected =
|
||||||
|
[yaml|
|
||||||
|
data:
|
||||||
|
hello_world_function:
|
||||||
|
- one: "hello"
|
||||||
|
two: "world"
|
||||||
|
|]
|
||||||
|
|
||||||
|
actual :: IO Value
|
||||||
|
actual =
|
||||||
|
GraphqlEngine.postGraphql
|
||||||
|
testEnvironment
|
||||||
|
[graphql|
|
||||||
|
query {
|
||||||
|
hello_world_function (
|
||||||
|
distinct_on: [one]
|
||||||
|
order_by: [{one:asc}]
|
||||||
|
){
|
||||||
|
one
|
||||||
|
two
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|]
|
||||||
|
|
||||||
|
shouldReturnYaml (options testEnvironment) actual expected
|
||||||
|
|
||||||
|
it "Runs a simple query that takes no parameters" $ \testEnvironment -> do
|
||||||
|
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
|
||||||
|
sourceName = BackendType.backendSourceName backendTypeMetadata
|
||||||
|
|
||||||
|
Schema.trackLogicalModel sourceName helloWorldLogicalModel testEnvironment
|
||||||
|
|
||||||
|
let expected =
|
||||||
|
[yaml|
|
||||||
|
data:
|
||||||
|
hello_world_function:
|
||||||
|
- one: "hello"
|
||||||
|
two: "world"
|
||||||
|
|]
|
||||||
|
|
||||||
|
actual :: IO Value
|
||||||
|
actual =
|
||||||
|
GraphqlEngine.postGraphql
|
||||||
|
testEnvironment
|
||||||
|
[graphql|
|
||||||
|
query {
|
||||||
|
hello_world_function(where: {one: {_eq: "hello"}}) {
|
||||||
|
one
|
||||||
|
two
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|]
|
||||||
|
|
||||||
|
shouldReturnYaml (options testEnvironment) actual expected
|
||||||
|
|
||||||
|
it "Runs a simple query that takes one dummy parameter and order_by" $ \testEnvironment -> do
|
||||||
|
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
|
||||||
|
source = BackendType.backendSourceName backendTypeMetadata
|
||||||
|
|
||||||
|
helloWorldLogicalModelWithDummyArgument :: Schema.LogicalModel
|
||||||
|
helloWorldLogicalModelWithDummyArgument =
|
||||||
|
(Schema.logicalModel "hello_world_function_with_dummy" query)
|
||||||
|
{ Schema.logicalModelColumns =
|
||||||
|
[ Schema.logicalModelColumn "one" Schema.TStr,
|
||||||
|
Schema.logicalModelColumn "two" Schema.TStr
|
||||||
|
],
|
||||||
|
Schema.logicalModelArguments =
|
||||||
|
[ Schema.logicalModelColumn "dummy" Schema.TStr
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema.trackLogicalModel source helloWorldLogicalModelWithDummyArgument testEnvironment
|
||||||
|
|
||||||
|
let expected =
|
||||||
|
[yaml|
|
||||||
|
data:
|
||||||
|
hello_world_function_with_dummy:
|
||||||
|
- two: "world"
|
||||||
|
- two: "friend"
|
||||||
|
|]
|
||||||
|
|
||||||
|
actual :: IO Value
|
||||||
|
actual =
|
||||||
|
GraphqlEngine.postGraphql
|
||||||
|
testEnvironment
|
||||||
|
[graphql|
|
||||||
|
query {
|
||||||
|
hello_world_function_with_dummy(args: {dummy: "ignored"}, order_by: {one: asc}) {
|
||||||
|
two
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|]
|
||||||
|
|
||||||
|
shouldReturnYaml (options testEnvironment) actual expected
|
||||||
|
|
||||||
|
it "Runs a simple query that takes no parameters but ends with a comment" $ \testEnvironment -> do
|
||||||
|
let spicyQuery :: Text
|
||||||
|
spicyQuery = "SELECT * FROM UNNEST([STRUCT('hello' as one, 'world' as two), ('welcome', 'friend')]) -- my query"
|
||||||
|
|
||||||
|
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
|
||||||
|
source = BackendType.backendSourceName backendTypeMetadata
|
||||||
|
|
||||||
|
helloCommentLogicalModel :: Schema.LogicalModel
|
||||||
|
helloCommentLogicalModel =
|
||||||
|
(Schema.logicalModel "hello_comment_function" spicyQuery)
|
||||||
|
{ Schema.logicalModelColumns =
|
||||||
|
[ Schema.logicalModelColumn "one" Schema.TStr,
|
||||||
|
Schema.logicalModelColumn "two" Schema.TStr
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema.trackLogicalModel source helloCommentLogicalModel testEnvironment
|
||||||
|
|
||||||
|
let expected =
|
||||||
|
[yaml|
|
||||||
|
data:
|
||||||
|
hello_comment_function:
|
||||||
|
- one: "hello"
|
||||||
|
two: "world"
|
||||||
|
- one: "welcome"
|
||||||
|
two: "friend"
|
||||||
|
|]
|
||||||
|
|
||||||
|
actual :: IO Value
|
||||||
|
actual =
|
||||||
|
GraphqlEngine.postGraphql
|
||||||
|
testEnvironment
|
||||||
|
[graphql|
|
||||||
|
query {
|
||||||
|
hello_comment_function {
|
||||||
|
one
|
||||||
|
two
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|]
|
||||||
|
|
||||||
|
shouldReturnYaml (options testEnvironment) actual expected
|
||||||
|
|
||||||
|
it "Uses a column permission that we are allowed to access" $ \testEnvironment -> do
|
||||||
|
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
|
||||||
|
source = BackendType.backendSourceName backendTypeMetadata
|
||||||
|
backendType = BackendType.backendTypeString backendTypeMetadata
|
||||||
|
createPermRequestType = backendType <> "_create_logical_model_select_permission"
|
||||||
|
|
||||||
|
helloWorldPermLogicalModel :: Schema.LogicalModel
|
||||||
|
helloWorldPermLogicalModel =
|
||||||
|
(Schema.logicalModel "hello_world_perms" query)
|
||||||
|
{ Schema.logicalModelColumns =
|
||||||
|
[ Schema.logicalModelColumn "one" Schema.TStr,
|
||||||
|
Schema.logicalModelColumn "two" Schema.TStr
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema.trackLogicalModel source helloWorldPermLogicalModel testEnvironment
|
||||||
|
|
||||||
|
shouldReturnYaml
|
||||||
|
(options testEnvironment)
|
||||||
|
( GraphqlEngine.postMetadata
|
||||||
|
testEnvironment
|
||||||
|
[yaml|
|
||||||
|
type: bulk
|
||||||
|
args:
|
||||||
|
- type: *createPermRequestType
|
||||||
|
args:
|
||||||
|
source: *source
|
||||||
|
root_field_name: hello_world_perms
|
||||||
|
role: "test"
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- one
|
||||||
|
filter: {}
|
||||||
|
|]
|
||||||
|
)
|
||||||
|
[yaml|
|
||||||
|
- message: success
|
||||||
|
|]
|
||||||
|
|
||||||
|
let expected =
|
||||||
|
[yaml|
|
||||||
|
data:
|
||||||
|
hello_world_perms:
|
||||||
|
- one: "hello"
|
||||||
|
- one: "welcome"
|
||||||
|
|]
|
||||||
|
|
||||||
|
actual :: IO Value
|
||||||
|
actual =
|
||||||
|
GraphqlEngine.postGraphqlWithHeaders
|
||||||
|
testEnvironment
|
||||||
|
[("X-Hasura-Role", "test")]
|
||||||
|
[graphql|
|
||||||
|
query {
|
||||||
|
hello_world_perms {
|
||||||
|
one
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|]
|
||||||
|
|
||||||
|
shouldReturnYaml (options testEnvironment) actual expected
|
||||||
|
|
||||||
|
it "Fails because we access a column we do not have permissions for" $ \testEnvironment -> do
|
||||||
|
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
|
||||||
|
source = BackendType.backendSourceName backendTypeMetadata
|
||||||
|
backendType = BackendType.backendTypeString backendTypeMetadata
|
||||||
|
createPermRequestType = backendType <> "_create_logical_model_select_permission"
|
||||||
|
|
||||||
|
helloWorldPermLogicalModel :: Schema.LogicalModel
|
||||||
|
helloWorldPermLogicalModel =
|
||||||
|
(Schema.logicalModel "hello_world_perms" query)
|
||||||
|
{ Schema.logicalModelColumns =
|
||||||
|
[ Schema.logicalModelColumn "one" Schema.TStr,
|
||||||
|
Schema.logicalModelColumn "two" Schema.TStr
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema.trackLogicalModel source helloWorldPermLogicalModel testEnvironment
|
||||||
|
|
||||||
|
shouldReturnYaml
|
||||||
|
(options testEnvironment)
|
||||||
|
( GraphqlEngine.postMetadata
|
||||||
|
testEnvironment
|
||||||
|
[yaml|
|
||||||
|
type: bulk
|
||||||
|
args:
|
||||||
|
- type: *createPermRequestType
|
||||||
|
args:
|
||||||
|
source: *source
|
||||||
|
root_field_name: hello_world_perms
|
||||||
|
role: "test"
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- two
|
||||||
|
filter: {}
|
||||||
|
|]
|
||||||
|
)
|
||||||
|
[yaml|
|
||||||
|
- message: success
|
||||||
|
|]
|
||||||
|
|
||||||
|
let expected =
|
||||||
|
[yaml|
|
||||||
|
errors:
|
||||||
|
- extensions:
|
||||||
|
code: validation-failed
|
||||||
|
path: $.selectionSet.hello_world_perms.selectionSet.one
|
||||||
|
message: "field 'one' not found in type: 'hello_world_perms'"
|
||||||
|
|]
|
||||||
|
|
||||||
|
actual :: IO Value
|
||||||
|
actual =
|
||||||
|
GraphqlEngine.postGraphqlWithHeaders
|
||||||
|
testEnvironment
|
||||||
|
[("X-Hasura-Role", "test")]
|
||||||
|
[graphql|
|
||||||
|
query {
|
||||||
|
hello_world_perms {
|
||||||
|
one
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|]
|
||||||
|
|
||||||
|
shouldReturnYaml (options testEnvironment) actual expected
|
||||||
|
|
||||||
|
it "Using row permissions filters out some results" $ \testEnvironment -> do
|
||||||
|
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
|
||||||
|
source = BackendType.backendSourceName backendTypeMetadata
|
||||||
|
backendType = BackendType.backendTypeString backendTypeMetadata
|
||||||
|
createPermRequestType = backendType <> "_create_logical_model_select_permission"
|
||||||
|
|
||||||
|
helloWorldPermLogicalModel :: Schema.LogicalModel
|
||||||
|
helloWorldPermLogicalModel =
|
||||||
|
(Schema.logicalModel "hello_world_perms" query)
|
||||||
|
{ Schema.logicalModelColumns =
|
||||||
|
[ Schema.logicalModelColumn "one" Schema.TStr,
|
||||||
|
Schema.logicalModelColumn "two" Schema.TStr
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema.trackLogicalModel source helloWorldPermLogicalModel testEnvironment
|
||||||
|
|
||||||
|
shouldReturnYaml
|
||||||
|
(options testEnvironment)
|
||||||
|
( GraphqlEngine.postMetadata
|
||||||
|
testEnvironment
|
||||||
|
[yaml|
|
||||||
|
type: bulk
|
||||||
|
args:
|
||||||
|
- type: *createPermRequestType
|
||||||
|
args:
|
||||||
|
source: *source
|
||||||
|
root_field_name: hello_world_perms
|
||||||
|
role: "test"
|
||||||
|
permission:
|
||||||
|
columns: "*"
|
||||||
|
filter:
|
||||||
|
one:
|
||||||
|
_eq: "welcome"
|
||||||
|
|]
|
||||||
|
)
|
||||||
|
[yaml|
|
||||||
|
- message: success
|
||||||
|
|]
|
||||||
|
|
||||||
|
let expected =
|
||||||
|
[yaml|
|
||||||
|
data:
|
||||||
|
hello_world_perms:
|
||||||
|
- one: "welcome"
|
||||||
|
two: "friend"
|
||||||
|
|]
|
||||||
|
|
||||||
|
actual :: IO Value
|
||||||
|
actual =
|
||||||
|
GraphqlEngine.postGraphqlWithHeaders
|
||||||
|
testEnvironment
|
||||||
|
[("X-Hasura-Role", "test")]
|
||||||
|
[graphql|
|
||||||
|
query {
|
||||||
|
hello_world_perms {
|
||||||
|
one
|
||||||
|
two
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|]
|
||||||
|
|
||||||
|
shouldReturnYaml (options testEnvironment) actual expected
|
||||||
|
|
||||||
|
let articleQuery :: Schema.SchemaName -> Text
|
||||||
|
articleQuery schemaName =
|
||||||
|
[i|
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
( substring(content, 1, {{length}}) || (
|
||||||
|
case when length(content) < {{length}}
|
||||||
|
then ''
|
||||||
|
else '...' end
|
||||||
|
)) as excerpt,
|
||||||
|
date
|
||||||
|
from #{unSchemaName schemaName}.article
|
||||||
|
|]
|
||||||
|
|
||||||
|
describe "Testing Logical Models" $ do
|
||||||
|
it "Runs a simple query that takes one parameter and uses it multiple times" $ \testEnvironment -> do
|
||||||
|
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
|
||||||
|
source = BackendType.backendSourceName backendTypeMetadata
|
||||||
|
|
||||||
|
schemaName :: Schema.SchemaName
|
||||||
|
schemaName = Schema.getSchemaName testEnvironment
|
||||||
|
|
||||||
|
articleWithExcerptLogicalModel :: Schema.LogicalModel
|
||||||
|
articleWithExcerptLogicalModel =
|
||||||
|
(Schema.logicalModel "article_with_excerpt" (articleQuery schemaName))
|
||||||
|
{ Schema.logicalModelColumns =
|
||||||
|
[ Schema.logicalModelColumn "id" Schema.TInt,
|
||||||
|
Schema.logicalModelColumn "title" Schema.TStr,
|
||||||
|
Schema.logicalModelColumn "excerpt" Schema.TStr,
|
||||||
|
Schema.logicalModelColumn "date" Schema.TUTCTime
|
||||||
|
],
|
||||||
|
Schema.logicalModelArguments =
|
||||||
|
[ Schema.logicalModelColumn "length" Schema.TInt
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema.trackLogicalModel source articleWithExcerptLogicalModel testEnvironment
|
||||||
|
|
||||||
|
let actual :: IO Value
|
||||||
|
actual =
|
||||||
|
GraphqlEngine.postGraphql
|
||||||
|
testEnvironment
|
||||||
|
[graphql|
|
||||||
|
query {
|
||||||
|
article_with_excerpt(args: { length: 34 }) {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
date
|
||||||
|
excerpt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|]
|
||||||
|
|
||||||
|
expected =
|
||||||
|
[yaml|
|
||||||
|
data:
|
||||||
|
article_with_excerpt:
|
||||||
|
- id: '1'
|
||||||
|
title: "Dogs"
|
||||||
|
date: "2000-01-01T00:00:00"
|
||||||
|
excerpt: "I like to eat dog food I am a dogs..."
|
||||||
|
|]
|
||||||
|
|
||||||
|
shouldReturnYaml (options testEnvironment) actual expected
|
||||||
|
|
||||||
|
it "Uses two queries with the same argument names and ensure they don't mess with one another" $ \testEnvironment -> do
|
||||||
|
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
|
||||||
|
source = BackendType.backendSourceName backendTypeMetadata
|
||||||
|
|
||||||
|
schemaName :: Schema.SchemaName
|
||||||
|
schemaName = Schema.getSchemaName testEnvironment
|
||||||
|
|
||||||
|
mkArticleWithExcerptLogicalModel :: Text -> Schema.LogicalModel
|
||||||
|
mkArticleWithExcerptLogicalModel name =
|
||||||
|
(Schema.logicalModel name (articleQuery schemaName))
|
||||||
|
{ Schema.logicalModelColumns =
|
||||||
|
[ Schema.logicalModelColumn "id" Schema.TInt,
|
||||||
|
Schema.logicalModelColumn "title" Schema.TStr,
|
||||||
|
Schema.logicalModelColumn "excerpt" Schema.TStr,
|
||||||
|
Schema.logicalModelColumn "date" Schema.TUTCTime
|
||||||
|
],
|
||||||
|
Schema.logicalModelArguments =
|
||||||
|
[ Schema.logicalModelColumn "length" Schema.TInt
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema.trackLogicalModel
|
||||||
|
source
|
||||||
|
(mkArticleWithExcerptLogicalModel "article_with_excerpt_1")
|
||||||
|
testEnvironment
|
||||||
|
|
||||||
|
Schema.trackLogicalModel
|
||||||
|
source
|
||||||
|
(mkArticleWithExcerptLogicalModel "article_with_excerpt_2")
|
||||||
|
testEnvironment
|
||||||
|
|
||||||
|
let actual :: IO Value
|
||||||
|
actual =
|
||||||
|
GraphqlEngine.postGraphql
|
||||||
|
testEnvironment
|
||||||
|
[graphql|
|
||||||
|
query {
|
||||||
|
article_with_excerpt_1(args: { length: 34 }) {
|
||||||
|
excerpt
|
||||||
|
}
|
||||||
|
article_with_excerpt_2(args: { length: 13 }) {
|
||||||
|
excerpt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|]
|
||||||
|
|
||||||
|
expected =
|
||||||
|
[yaml|
|
||||||
|
data:
|
||||||
|
article_with_excerpt_1:
|
||||||
|
- excerpt: "I like to eat dog food I am a dogs..."
|
||||||
|
article_with_excerpt_2:
|
||||||
|
- excerpt: "I like to eat..."
|
||||||
|
|]
|
||||||
|
|
||||||
|
shouldReturnYaml (options testEnvironment) actual expected
|
||||||
|
|
||||||
|
it "Uses a one parameter query and uses it multiple times" $ \testEnvironment -> do
|
||||||
|
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
|
||||||
|
source = BackendType.backendSourceName backendTypeMetadata
|
||||||
|
|
||||||
|
schemaName :: Schema.SchemaName
|
||||||
|
schemaName = Schema.getSchemaName testEnvironment
|
||||||
|
|
||||||
|
articleWithExcerptLogicalModel :: Schema.LogicalModel
|
||||||
|
articleWithExcerptLogicalModel =
|
||||||
|
(Schema.logicalModel "article_with_excerpt" (articleQuery schemaName))
|
||||||
|
{ Schema.logicalModelColumns =
|
||||||
|
[ Schema.logicalModelColumn "id" Schema.TInt,
|
||||||
|
Schema.logicalModelColumn "title" Schema.TStr,
|
||||||
|
Schema.logicalModelColumn "excerpt" Schema.TStr,
|
||||||
|
Schema.logicalModelColumn "date" Schema.TUTCTime
|
||||||
|
],
|
||||||
|
Schema.logicalModelArguments =
|
||||||
|
[ Schema.logicalModelColumn "length" Schema.TInt
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema.trackLogicalModel source articleWithExcerptLogicalModel testEnvironment
|
||||||
|
|
||||||
|
let actual :: IO Value
|
||||||
|
actual =
|
||||||
|
GraphqlEngine.postGraphql
|
||||||
|
testEnvironment
|
||||||
|
[graphql|
|
||||||
|
query {
|
||||||
|
first: article_with_excerpt(args: { length: 34 }) {
|
||||||
|
excerpt
|
||||||
|
}
|
||||||
|
second: article_with_excerpt(args: { length: 13 }) {
|
||||||
|
excerpt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|]
|
||||||
|
|
||||||
|
expected =
|
||||||
|
[yaml|
|
||||||
|
data:
|
||||||
|
first:
|
||||||
|
- excerpt: "I like to eat dog food I am a dogs..."
|
||||||
|
second:
|
||||||
|
- excerpt: "I like to eat..."
|
||||||
|
|]
|
||||||
|
|
||||||
|
shouldReturnYaml (options testEnvironment) actual expected
|
||||||
|
|
||||||
|
it "Uses a one parameter query, passing it a GraphQL variable" $ \testEnvironment -> do
|
||||||
|
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
|
||||||
|
source = BackendType.backendSourceName backendTypeMetadata
|
||||||
|
|
||||||
|
schemaName :: Schema.SchemaName
|
||||||
|
schemaName = Schema.getSchemaName testEnvironment
|
||||||
|
|
||||||
|
articleWithExcerptLogicalModel :: Schema.LogicalModel
|
||||||
|
articleWithExcerptLogicalModel =
|
||||||
|
(Schema.logicalModel "article_with_excerpt" (articleQuery schemaName))
|
||||||
|
{ Schema.logicalModelColumns =
|
||||||
|
[ Schema.logicalModelColumn "id" Schema.TInt,
|
||||||
|
Schema.logicalModelColumn "title" Schema.TStr,
|
||||||
|
Schema.logicalModelColumn "excerpt" Schema.TStr,
|
||||||
|
Schema.logicalModelColumn "date" Schema.TUTCTime
|
||||||
|
],
|
||||||
|
Schema.logicalModelArguments =
|
||||||
|
[ Schema.logicalModelColumn "length" Schema.TInt
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema.trackLogicalModel source articleWithExcerptLogicalModel testEnvironment
|
||||||
|
|
||||||
|
let variables =
|
||||||
|
[yaml|
|
||||||
|
length: 34
|
||||||
|
|]
|
||||||
|
|
||||||
|
actual :: IO Value
|
||||||
|
actual =
|
||||||
|
GraphqlEngine.postGraphqlWithVariables
|
||||||
|
testEnvironment
|
||||||
|
[graphql|
|
||||||
|
query MyQuery($length: Int!) {
|
||||||
|
article_with_excerpt(args: { length: $length }) {
|
||||||
|
excerpt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|]
|
||||||
|
variables
|
||||||
|
|
||||||
|
expected =
|
||||||
|
[yaml|
|
||||||
|
data:
|
||||||
|
article_with_excerpt:
|
||||||
|
- excerpt: "I like to eat dog food I am a dogs..."
|
||||||
|
|]
|
||||||
|
|
||||||
|
shouldReturnYaml (options testEnvironment) actual expected
|
@ -1,4 +1,5 @@
|
|||||||
{-# HLINT ignore "Use onNothing" #-}
|
{-# HLINT ignore "Use onNothing" #-}
|
||||||
|
{-# LANGUAGE DeriveDataTypeable #-}
|
||||||
{-# LANGUAGE TemplateHaskellQuotes #-}
|
{-# LANGUAGE TemplateHaskellQuotes #-}
|
||||||
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
|
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ import Control.DeepSeq (NFData)
|
|||||||
import Data.Aeson qualified as J
|
import Data.Aeson qualified as J
|
||||||
import Data.Char qualified as C
|
import Data.Char qualified as C
|
||||||
import Data.Coerce (coerce)
|
import Data.Coerce (coerce)
|
||||||
|
import Data.Data (Data)
|
||||||
import Data.Hashable (Hashable)
|
import Data.Hashable (Hashable)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import Data.Text qualified as T
|
import Data.Text qualified as T
|
||||||
@ -42,7 +44,7 @@ import Prelude
|
|||||||
|
|
||||||
-- Defined here and re-exported in the public module to avoid exporting `unName`.`
|
-- Defined here and re-exported in the public module to avoid exporting `unName`.`
|
||||||
newtype Name = Name {unName :: Text}
|
newtype Name = Name {unName :: Text}
|
||||||
deriving stock (Eq, Lift, Ord, Show)
|
deriving stock (Data, Eq, Lift, Ord, Show)
|
||||||
deriving newtype (Semigroup, Hashable, NFData, Pretty, J.ToJSONKey, J.ToJSON)
|
deriving newtype (Semigroup, Hashable, NFData, Pretty, J.ToJSONKey, J.ToJSON)
|
||||||
|
|
||||||
instance HasCodec Name where
|
instance HasCodec Name where
|
||||||
|
@ -6,6 +6,7 @@ module Hasura.Backends.BigQuery.FromIr
|
|||||||
Error (..),
|
Error (..),
|
||||||
runFromIr,
|
runFromIr,
|
||||||
FromIr,
|
FromIr,
|
||||||
|
FromIrWriter (..),
|
||||||
FromIrConfig (..),
|
FromIrConfig (..),
|
||||||
defaultFromIrConfig,
|
defaultFromIrConfig,
|
||||||
bigQuerySourceConfigToFromIrConfig,
|
bigQuerySourceConfigToFromIrConfig,
|
||||||
@ -21,9 +22,13 @@ import Data.List.NonEmpty qualified as NE
|
|||||||
import Data.Map.Strict (Map)
|
import Data.Map.Strict (Map)
|
||||||
import Data.Map.Strict qualified as M
|
import Data.Map.Strict qualified as M
|
||||||
import Data.Text qualified as T
|
import Data.Text qualified as T
|
||||||
|
import Data.Text.Extended qualified as T (toTxt)
|
||||||
import Hasura.Backends.BigQuery.Instances.Types ()
|
import Hasura.Backends.BigQuery.Instances.Types ()
|
||||||
import Hasura.Backends.BigQuery.Source (BigQuerySourceConfig (..))
|
import Hasura.Backends.BigQuery.Source (BigQuerySourceConfig (..))
|
||||||
import Hasura.Backends.BigQuery.Types as BigQuery
|
import Hasura.Backends.BigQuery.Types as BigQuery
|
||||||
|
import Hasura.LogicalModel.IR (LogicalModel (..))
|
||||||
|
import Hasura.LogicalModel.Metadata (InterpolatedQuery)
|
||||||
|
import Hasura.LogicalModel.Types (LogicalModelName (..))
|
||||||
import Hasura.Prelude
|
import Hasura.Prelude
|
||||||
import Hasura.RQL.IR qualified as Ir
|
import Hasura.RQL.IR qualified as Ir
|
||||||
import Hasura.RQL.Types.Column qualified as Rql
|
import Hasura.RQL.Types.Column qualified as Rql
|
||||||
@ -100,10 +105,28 @@ instance Show Error where
|
|||||||
-- setting the current entity that a given field name refers to. See
|
-- setting the current entity that a given field name refers to. See
|
||||||
-- @fromColumn@.
|
-- @fromColumn@.
|
||||||
newtype FromIr a = FromIr
|
newtype FromIr a = FromIr
|
||||||
{ unFromIr :: ReaderT FromIrReader (StateT FromIrState (Validate (NonEmpty Error))) a
|
{ unFromIr ::
|
||||||
|
ReaderT
|
||||||
|
FromIrReader
|
||||||
|
( StateT
|
||||||
|
FromIrState
|
||||||
|
( WriterT
|
||||||
|
FromIrWriter
|
||||||
|
( Validate (NonEmpty Error)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
a
|
||||||
}
|
}
|
||||||
deriving (Functor, Applicative, Monad, MonadValidate (NonEmpty Error))
|
deriving (Functor, Applicative, Monad, MonadValidate (NonEmpty Error))
|
||||||
|
|
||||||
|
-- | Collected from using a logical model in a query.
|
||||||
|
-- Each entry here because a CTE to be prepended to the query.
|
||||||
|
newtype FromIrWriter = FromIrWriter
|
||||||
|
{ fromIrWriterLogicalModels :: Map LogicalModelName (InterpolatedQuery Expression)
|
||||||
|
}
|
||||||
|
deriving newtype (Semigroup, Monoid)
|
||||||
|
|
||||||
data FromIrState = FromIrState
|
data FromIrState = FromIrState
|
||||||
{ indices :: Map Text Int
|
{ indices :: Map Text Int
|
||||||
}
|
}
|
||||||
@ -160,11 +183,12 @@ data ParentSelectFromEntity
|
|||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Runners
|
-- Runners
|
||||||
|
|
||||||
runFromIr :: FromIrConfig -> FromIr a -> Validate (NonEmpty Error) a
|
runFromIr :: FromIrConfig -> FromIr a -> Validate (NonEmpty Error) (a, FromIrWriter)
|
||||||
runFromIr config fromIr =
|
runFromIr config fromIr =
|
||||||
evalStateT
|
runWriterT $
|
||||||
(runReaderT (unFromIr fromIr) (FromIrReader {config}))
|
evalStateT
|
||||||
(FromIrState {indices = mempty})
|
(runReaderT (unFromIr fromIr) (FromIrReader {config}))
|
||||||
|
(FromIrState {indices = mempty})
|
||||||
|
|
||||||
bigQuerySourceConfigToFromIrConfig :: BigQuerySourceConfig -> FromIrConfig
|
bigQuerySourceConfigToFromIrConfig :: BigQuerySourceConfig -> FromIrConfig
|
||||||
bigQuerySourceConfigToFromIrConfig BigQuerySourceConfig {_scGlobalSelectLimit} =
|
bigQuerySourceConfigToFromIrConfig BigQuerySourceConfig {_scGlobalSelectLimit} =
|
||||||
@ -237,6 +261,7 @@ fromSelectRows parentSelectFromEntity annSelectG = do
|
|||||||
| functionName nm == "unnest" -> fromUnnestedJSON json columns (map fst fields)
|
| functionName nm == "unnest" -> fromUnnestedJSON json columns (map fst fields)
|
||||||
Ir.FromFunction functionName (Rql.FunctionArgsExp positionalArgs namedArgs) Nothing ->
|
Ir.FromFunction functionName (Rql.FunctionArgsExp positionalArgs namedArgs) Nothing ->
|
||||||
fromFunction parentSelectFromEntity functionName positionalArgs namedArgs
|
fromFunction parentSelectFromEntity functionName positionalArgs namedArgs
|
||||||
|
Ir.FromLogicalModel logicalModel -> fromLogicalModel logicalModel
|
||||||
_ -> refute (pure (FromTypeUnsupported from))
|
_ -> refute (pure (FromTypeUnsupported from))
|
||||||
Args
|
Args
|
||||||
{ argsOrderBy,
|
{ argsOrderBy,
|
||||||
@ -258,7 +283,8 @@ fromSelectRows parentSelectFromEntity annSelectG = do
|
|||||||
globalTop <- getGlobalTop
|
globalTop <- getGlobalTop
|
||||||
let select =
|
let select =
|
||||||
Select
|
Select
|
||||||
{ selectCardinality = Many,
|
{ selectWith = Nothing,
|
||||||
|
selectCardinality = Many,
|
||||||
selectAsStruct = NoAsStruct,
|
selectAsStruct = NoAsStruct,
|
||||||
selectFinalWantedFields = pure (fieldTextNames fields),
|
selectFinalWantedFields = pure (fieldTextNames fields),
|
||||||
selectGroupBy = mempty,
|
selectGroupBy = mempty,
|
||||||
@ -441,7 +467,8 @@ fromSelectAggregate minnerJoinFields annSelectG = do
|
|||||||
}
|
}
|
||||||
pure
|
pure
|
||||||
Select
|
Select
|
||||||
{ selectCardinality = One,
|
{ selectWith = Nothing,
|
||||||
|
selectCardinality = One,
|
||||||
selectAsStruct = NoAsStruct,
|
selectAsStruct = NoAsStruct,
|
||||||
selectFinalWantedFields = Nothing,
|
selectFinalWantedFields = Nothing,
|
||||||
selectGroupBy = mempty,
|
selectGroupBy = mempty,
|
||||||
@ -452,7 +479,8 @@ fromSelectAggregate minnerJoinFields annSelectG = do
|
|||||||
( Aliased
|
( Aliased
|
||||||
{ aliasedThing =
|
{ aliasedThing =
|
||||||
Select
|
Select
|
||||||
{ selectProjections = innerProjections,
|
{ selectWith = Nothing,
|
||||||
|
selectProjections = innerProjections,
|
||||||
selectAsStruct = NoAsStruct,
|
selectAsStruct = NoAsStruct,
|
||||||
selectFrom,
|
selectFrom,
|
||||||
selectJoins = argsJoins,
|
selectJoins = argsJoins,
|
||||||
@ -611,7 +639,8 @@ unfurlAnnotatedOrderByElement =
|
|||||||
{ joinSource =
|
{ joinSource =
|
||||||
JoinSelect
|
JoinSelect
|
||||||
Select
|
Select
|
||||||
{ selectCardinality = One,
|
{ selectWith = Nothing,
|
||||||
|
selectCardinality = One,
|
||||||
selectAsStruct = NoAsStruct,
|
selectAsStruct = NoAsStruct,
|
||||||
selectFinalWantedFields = Nothing,
|
selectFinalWantedFields = Nothing,
|
||||||
selectGroupBy = mempty,
|
selectGroupBy = mempty,
|
||||||
@ -663,7 +692,8 @@ unfurlAnnotatedOrderByElement =
|
|||||||
{ joinSource =
|
{ joinSource =
|
||||||
JoinSelect
|
JoinSelect
|
||||||
Select
|
Select
|
||||||
{ selectCardinality = One,
|
{ selectWith = Nothing,
|
||||||
|
selectCardinality = One,
|
||||||
selectAsStruct = NoAsStruct,
|
selectAsStruct = NoAsStruct,
|
||||||
selectFinalWantedFields = Nothing,
|
selectFinalWantedFields = Nothing,
|
||||||
selectTop = NoTop,
|
selectTop = NoTop,
|
||||||
@ -771,6 +801,11 @@ fromAnnBoolExp ::
|
|||||||
ReaderT EntityAlias FromIr Expression
|
ReaderT EntityAlias FromIr Expression
|
||||||
fromAnnBoolExp = traverse fromAnnBoolExpFld >=> fromGBoolExp
|
fromAnnBoolExp = traverse fromAnnBoolExpFld >=> fromGBoolExp
|
||||||
|
|
||||||
|
fromLogicalModel :: LogicalModel 'BigQuery Expression -> FromIr From
|
||||||
|
fromLogicalModel LogicalModel {..} = FromIr do
|
||||||
|
tell (FromIrWriter (M.singleton lmRootFieldName lmInterpolatedQuery))
|
||||||
|
pure (FromLogicalModel lmRootFieldName)
|
||||||
|
|
||||||
fromAnnBoolExpFld ::
|
fromAnnBoolExpFld ::
|
||||||
Ir.AnnBoolExpFld 'BigQuery Expression -> ReaderT EntityAlias FromIr Expression
|
Ir.AnnBoolExpFld 'BigQuery Expression -> ReaderT EntityAlias FromIr Expression
|
||||||
fromAnnBoolExpFld =
|
fromAnnBoolExpFld =
|
||||||
@ -787,7 +822,8 @@ fromAnnBoolExpFld =
|
|||||||
pure
|
pure
|
||||||
( ExistsExpression
|
( ExistsExpression
|
||||||
Select
|
Select
|
||||||
{ selectCardinality = One,
|
{ selectWith = Nothing,
|
||||||
|
selectCardinality = One,
|
||||||
selectAsStruct = NoAsStruct,
|
selectAsStruct = NoAsStruct,
|
||||||
selectFinalWantedFields = Nothing,
|
selectFinalWantedFields = Nothing,
|
||||||
selectGroupBy = mempty,
|
selectGroupBy = mempty,
|
||||||
@ -826,7 +862,8 @@ fromGExists Ir.GExists {_geTable, _geWhere} = do
|
|||||||
local (const (fromAlias selectFrom)) (fromGBoolExp _geWhere)
|
local (const (fromAlias selectFrom)) (fromGBoolExp _geWhere)
|
||||||
pure
|
pure
|
||||||
Select
|
Select
|
||||||
{ selectCardinality = One,
|
{ selectWith = Nothing,
|
||||||
|
selectCardinality = One,
|
||||||
selectAsStruct = NoAsStruct,
|
selectAsStruct = NoAsStruct,
|
||||||
selectFinalWantedFields = Nothing,
|
selectFinalWantedFields = Nothing,
|
||||||
selectGroupBy = mempty,
|
selectGroupBy = mempty,
|
||||||
@ -1219,7 +1256,8 @@ fromObjectRelationSelectG _existingJoins annRelationSelectG = do
|
|||||||
joinSource =
|
joinSource =
|
||||||
JoinSelect
|
JoinSelect
|
||||||
Select
|
Select
|
||||||
{ selectCardinality = One,
|
{ selectWith = Nothing,
|
||||||
|
selectCardinality = One,
|
||||||
selectAsStruct = NoAsStruct,
|
selectAsStruct = NoAsStruct,
|
||||||
selectFinalWantedFields,
|
selectFinalWantedFields,
|
||||||
selectGroupBy = mempty,
|
selectGroupBy = mempty,
|
||||||
@ -1324,7 +1362,8 @@ fromComputedFieldSelect = \case
|
|||||||
wrapUnnest from =
|
wrapUnnest from =
|
||||||
let starSelect =
|
let starSelect =
|
||||||
Select
|
Select
|
||||||
{ selectTop = NoTop,
|
{ selectWith = Nothing,
|
||||||
|
selectTop = NoTop,
|
||||||
selectAsStruct = AsStruct,
|
selectAsStruct = AsStruct,
|
||||||
selectProjections = pure StarProjection,
|
selectProjections = pure StarProjection,
|
||||||
selectFrom = from,
|
selectFrom = from,
|
||||||
@ -1468,7 +1507,8 @@ fromArrayRelationSelectG annRelationSelectG = do
|
|||||||
|
|
||||||
let joinSelect =
|
let joinSelect =
|
||||||
Select
|
Select
|
||||||
{ selectCardinality = One,
|
{ selectWith = Nothing,
|
||||||
|
selectCardinality = One,
|
||||||
selectAsStruct = NoAsStruct,
|
selectAsStruct = NoAsStruct,
|
||||||
selectFinalWantedFields = selectFinalWantedFields select,
|
selectFinalWantedFields = selectFinalWantedFields select,
|
||||||
selectTop = NoTop,
|
selectTop = NoTop,
|
||||||
@ -1498,7 +1538,8 @@ fromArrayRelationSelectG annRelationSelectG = do
|
|||||||
{ aliasedAlias = coerce (fromAlias (selectFrom select)),
|
{ aliasedAlias = coerce (fromAlias (selectFrom select)),
|
||||||
aliasedThing =
|
aliasedThing =
|
||||||
Select
|
Select
|
||||||
{ selectProjections =
|
{ selectWith = Nothing,
|
||||||
|
selectProjections =
|
||||||
selectProjections select
|
selectProjections select
|
||||||
<> joinFieldProjections
|
<> joinFieldProjections
|
||||||
`appendToNonEmpty` foldMap @Maybe
|
`appendToNonEmpty` foldMap @Maybe
|
||||||
@ -1845,6 +1886,7 @@ fromAlias (FromQualifiedTable Aliased {aliasedAlias}) = EntityAlias aliasedAlias
|
|||||||
fromAlias (FromSelect Aliased {aliasedAlias}) = EntityAlias aliasedAlias
|
fromAlias (FromSelect Aliased {aliasedAlias}) = EntityAlias aliasedAlias
|
||||||
fromAlias (FromSelectJson Aliased {aliasedAlias}) = EntityAlias aliasedAlias
|
fromAlias (FromSelectJson Aliased {aliasedAlias}) = EntityAlias aliasedAlias
|
||||||
fromAlias (FromFunction Aliased {aliasedAlias}) = EntityAlias aliasedAlias
|
fromAlias (FromFunction Aliased {aliasedAlias}) = EntityAlias aliasedAlias
|
||||||
|
fromAlias (FromLogicalModel (LogicalModelName logicalModelName)) = EntityAlias (T.toTxt logicalModelName)
|
||||||
|
|
||||||
fieldTextNames :: Ir.AnnFieldsG 'BigQuery Void Expression -> [Text]
|
fieldTextNames :: Ir.AnnFieldsG 'BigQuery Void Expression -> [Text]
|
||||||
fieldTextNames = fmap (\(Rql.FieldName name, _) -> name)
|
fieldTextNames = fmap (\(Rql.FieldName name, _) -> name)
|
||||||
|
@ -33,6 +33,7 @@ import Hasura.GraphQL.Schema.Parser qualified as P
|
|||||||
import Hasura.GraphQL.Schema.Select
|
import Hasura.GraphQL.Schema.Select
|
||||||
import Hasura.GraphQL.Schema.Table
|
import Hasura.GraphQL.Schema.Table
|
||||||
import Hasura.GraphQL.Schema.Typename
|
import Hasura.GraphQL.Schema.Typename
|
||||||
|
import Hasura.LogicalModel.Schema (defaultBuildLogicalModelRootFields)
|
||||||
import Hasura.Name qualified as Name
|
import Hasura.Name qualified as Name
|
||||||
import Hasura.Prelude
|
import Hasura.Prelude
|
||||||
import Hasura.RQL.IR.BoolExp
|
import Hasura.RQL.IR.BoolExp
|
||||||
@ -63,6 +64,7 @@ instance BackendSchema 'BigQuery where
|
|||||||
buildFunctionQueryFields _ _ _ _ = pure []
|
buildFunctionQueryFields _ _ _ _ = pure []
|
||||||
buildFunctionRelayQueryFields _ _ _ _ _ = pure []
|
buildFunctionRelayQueryFields _ _ _ _ _ = pure []
|
||||||
buildFunctionMutationFields _ _ _ _ = pure []
|
buildFunctionMutationFields _ _ _ _ = pure []
|
||||||
|
buildLogicalModelRootFields = defaultBuildLogicalModelRootFields
|
||||||
|
|
||||||
-- backend extensions
|
-- backend extensions
|
||||||
relayExtension = Nothing
|
relayExtension = Nothing
|
||||||
@ -86,6 +88,10 @@ instance BackendTableSelectSchema 'BigQuery where
|
|||||||
selectTableAggregate = defaultSelectTableAggregate
|
selectTableAggregate = defaultSelectTableAggregate
|
||||||
tableSelectionSet = defaultTableSelectionSet
|
tableSelectionSet = defaultTableSelectionSet
|
||||||
|
|
||||||
|
instance BackendCustomTypeSelectSchema 'BigQuery where
|
||||||
|
logicalModelArguments = defaultLogicalModelArgs
|
||||||
|
logicalModelSelectionSet = defaultLogicalModelSelectionSet
|
||||||
|
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
-- Individual components
|
-- Individual components
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
{-# LANGUAGE MonadComprehensions #-}
|
||||||
|
|
||||||
-- | Planning T-SQL queries and subscriptions.
|
-- | Planning T-SQL queries and subscriptions.
|
||||||
module Hasura.Backends.BigQuery.Plan
|
module Hasura.Backends.BigQuery.Plan
|
||||||
( planNoPlan,
|
( planNoPlan,
|
||||||
@ -6,6 +8,8 @@ where
|
|||||||
|
|
||||||
import Control.Monad.Validate
|
import Control.Monad.Validate
|
||||||
import Data.Aeson.Text
|
import Data.Aeson.Text
|
||||||
|
import Data.List.NonEmpty qualified as NE
|
||||||
|
import Data.Map.Strict qualified as Map
|
||||||
import Data.Text.Extended
|
import Data.Text.Extended
|
||||||
import Data.Text.Lazy qualified as LT
|
import Data.Text.Lazy qualified as LT
|
||||||
import Hasura.Backends.BigQuery.FromIr as BigQuery
|
import Hasura.Backends.BigQuery.FromIr as BigQuery
|
||||||
@ -29,8 +33,20 @@ planNoPlan ::
|
|||||||
m Select
|
m Select
|
||||||
planNoPlan fromIrConfig userInfo queryDB = do
|
planNoPlan fromIrConfig userInfo queryDB = do
|
||||||
rootField <- traverse (prepareValueNoPlan (_uiSession userInfo)) queryDB
|
rootField <- traverse (prepareValueNoPlan (_uiSession userInfo)) queryDB
|
||||||
runValidate (BigQuery.runFromIr fromIrConfig (BigQuery.fromRootField rootField))
|
|
||||||
`onLeft` (E.throw400 E.NotSupported . (tshow :: NonEmpty Error -> Text))
|
(select, FromIrWriter {fromIrWriterLogicalModels}) <-
|
||||||
|
runValidate (BigQuery.runFromIr fromIrConfig (BigQuery.fromRootField rootField))
|
||||||
|
`onLeft` (E.throw400 E.NotSupported . (tshow :: NonEmpty Error -> Text))
|
||||||
|
|
||||||
|
-- Logical models used within this query need to be converted into CTEs.
|
||||||
|
-- These need to come before any other CTEs in case those CTEs also depend on
|
||||||
|
-- the logical models.
|
||||||
|
let logicalModels :: Maybe With
|
||||||
|
logicalModels = do
|
||||||
|
ctes <- NE.nonEmpty (Map.toList fromIrWriterLogicalModels)
|
||||||
|
pure (With [Aliased query (toTxt name) | (name, query) <- ctes])
|
||||||
|
|
||||||
|
pure select {selectWith = logicalModels <> selectWith select}
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Resolving values
|
-- Resolving values
|
||||||
|
@ -25,12 +25,15 @@ import Data.List (intersperse)
|
|||||||
import Data.List.NonEmpty qualified as NE
|
import Data.List.NonEmpty qualified as NE
|
||||||
import Data.String
|
import Data.String
|
||||||
import Data.Text qualified as T
|
import Data.Text qualified as T
|
||||||
|
import Data.Text.Extended qualified as T (toTxt)
|
||||||
import Data.Text.Lazy qualified as LT
|
import Data.Text.Lazy qualified as LT
|
||||||
import Data.Text.Lazy.Builder (Builder)
|
import Data.Text.Lazy.Builder (Builder)
|
||||||
import Data.Text.Lazy.Builder qualified as LT
|
import Data.Text.Lazy.Builder qualified as LT
|
||||||
import Data.Tuple
|
import Data.Tuple
|
||||||
import Data.Vector qualified as V
|
import Data.Vector qualified as V
|
||||||
import Hasura.Backends.BigQuery.Types
|
import Hasura.Backends.BigQuery.Types
|
||||||
|
import Hasura.LogicalModel.Metadata (InterpolatedItem (..), InterpolatedQuery (..))
|
||||||
|
import Hasura.LogicalModel.Types (LogicalModelName (..))
|
||||||
import Hasura.Prelude hiding (second)
|
import Hasura.Prelude hiding (second)
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
@ -184,10 +187,26 @@ fromSelect Select {..} = finalExpression
|
|||||||
fromAsStruct = \case
|
fromAsStruct = \case
|
||||||
AsStruct -> "AS STRUCT"
|
AsStruct -> "AS STRUCT"
|
||||||
NoAsStruct -> ""
|
NoAsStruct -> ""
|
||||||
|
interpolatedQuery = \case
|
||||||
|
IIText t -> UnsafeTextPrinter t <+> NewlinePrinter
|
||||||
|
IIVariable v -> fromExpression v
|
||||||
|
fromWith = \case
|
||||||
|
Just (With expressions) -> do
|
||||||
|
let go :: InterpolatedQuery Expression -> Printer
|
||||||
|
go = foldr ((<+>) . interpolatedQuery) "" . getInterpolatedQuery
|
||||||
|
|
||||||
|
"WITH "
|
||||||
|
<+> SepByPrinter
|
||||||
|
("," <+> NewlinePrinter)
|
||||||
|
[ fromNameText alias <+> " AS " <+> parens (go thing)
|
||||||
|
| Aliased thing alias <- toList expressions
|
||||||
|
]
|
||||||
|
Nothing -> ""
|
||||||
inner =
|
inner =
|
||||||
SepByPrinter
|
SepByPrinter
|
||||||
NewlinePrinter
|
NewlinePrinter
|
||||||
[ "SELECT ",
|
[ fromWith selectWith,
|
||||||
|
"SELECT ",
|
||||||
fromAsStruct selectAsStruct,
|
fromAsStruct selectAsStruct,
|
||||||
IndentPrinter 7 projections,
|
IndentPrinter 7 projections,
|
||||||
"FROM " <+> IndentPrinter 5 (fromFrom selectFrom),
|
"FROM " <+> IndentPrinter 5 (fromFrom selectFrom),
|
||||||
@ -539,6 +558,8 @@ fromFrom =
|
|||||||
)
|
)
|
||||||
selectFromFunction
|
selectFromFunction
|
||||||
)
|
)
|
||||||
|
FromLogicalModel (LogicalModelName logicalModelName) ->
|
||||||
|
fromNameText (T.toTxt logicalModelName)
|
||||||
|
|
||||||
fromTableName :: TableName -> Printer
|
fromTableName :: TableName -> Printer
|
||||||
fromTableName TableName {tableName, tableNameSchema} =
|
fromTableName TableName {tableName, tableNameSchema} =
|
||||||
|
@ -50,6 +50,7 @@ module Hasura.Backends.BigQuery.Types
|
|||||||
Top (..),
|
Top (..),
|
||||||
Value (..),
|
Value (..),
|
||||||
Where (..),
|
Where (..),
|
||||||
|
With (..),
|
||||||
WindowFunction (..),
|
WindowFunction (..),
|
||||||
aggregateProjectionsFieldOrigin,
|
aggregateProjectionsFieldOrigin,
|
||||||
doubleToBigDecimal,
|
doubleToBigDecimal,
|
||||||
@ -96,6 +97,7 @@ import Data.Vector.Instances ()
|
|||||||
import Hasura.Base.Error
|
import Hasura.Base.Error
|
||||||
import Hasura.Base.ErrorValue qualified as ErrorValue
|
import Hasura.Base.ErrorValue qualified as ErrorValue
|
||||||
import Hasura.Base.ToErrorValue
|
import Hasura.Base.ToErrorValue
|
||||||
|
import Hasura.LogicalModel.Metadata (InterpolatedQuery, LogicalModelName)
|
||||||
import Hasura.Metadata.DTO.Utils (boundedEnumCodec)
|
import Hasura.Metadata.DTO.Utils (boundedEnumCodec)
|
||||||
import Hasura.Prelude hiding (state)
|
import Hasura.Prelude hiding (state)
|
||||||
import Hasura.RQL.IR.BoolExp
|
import Hasura.RQL.IR.BoolExp
|
||||||
@ -105,7 +107,8 @@ import Language.Haskell.TH.Syntax hiding (location)
|
|||||||
import Text.ParserCombinators.ReadP (eof, readP_to_S)
|
import Text.ParserCombinators.ReadP (eof, readP_to_S)
|
||||||
|
|
||||||
data Select = Select
|
data Select = Select
|
||||||
{ selectTop :: Top,
|
{ selectWith :: Maybe With,
|
||||||
|
selectTop :: Top,
|
||||||
selectAsStruct :: AsStruct,
|
selectAsStruct :: AsStruct,
|
||||||
selectProjections :: NonEmpty Projection,
|
selectProjections :: NonEmpty Projection,
|
||||||
selectFrom :: From,
|
selectFrom :: From,
|
||||||
@ -118,7 +121,7 @@ data Select = Select
|
|||||||
selectCardinality :: Cardinality
|
selectCardinality :: Cardinality
|
||||||
}
|
}
|
||||||
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
||||||
deriving anyclass (FromJSON, Hashable, NFData)
|
deriving anyclass (Hashable, NFData)
|
||||||
|
|
||||||
-- | Helper type allowing addition of extra fields used
|
-- | Helper type allowing addition of extra fields used
|
||||||
-- in PARTITION BY.
|
-- in PARTITION BY.
|
||||||
@ -149,14 +152,14 @@ data ArrayAgg = ArrayAgg
|
|||||||
arrayAggTop :: Top
|
arrayAggTop :: Top
|
||||||
}
|
}
|
||||||
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
||||||
deriving anyclass (FromJSON, Hashable, NFData)
|
deriving anyclass (Hashable, NFData)
|
||||||
|
|
||||||
data Reselect = Reselect
|
data Reselect = Reselect
|
||||||
{ reselectProjections :: NonEmpty Projection,
|
{ reselectProjections :: NonEmpty Projection,
|
||||||
reselectWhere :: Where
|
reselectWhere :: Where
|
||||||
}
|
}
|
||||||
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
||||||
deriving anyclass (FromJSON, Hashable, NFData)
|
deriving anyclass (Hashable, NFData)
|
||||||
|
|
||||||
data OrderBy = OrderBy
|
data OrderBy = OrderBy
|
||||||
{ orderByFieldName :: FieldName,
|
{ orderByFieldName :: FieldName,
|
||||||
@ -183,7 +186,7 @@ data FieldOrigin
|
|||||||
= NoOrigin
|
= NoOrigin
|
||||||
| AggregateOrigin [Aliased Aggregate]
|
| AggregateOrigin [Aliased Aggregate]
|
||||||
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
||||||
deriving anyclass (FromJSON, Hashable, NFData)
|
deriving anyclass (Hashable, NFData)
|
||||||
|
|
||||||
aggregateProjectionsFieldOrigin :: Projection -> FieldOrigin
|
aggregateProjectionsFieldOrigin :: Projection -> FieldOrigin
|
||||||
aggregateProjectionsFieldOrigin = \case
|
aggregateProjectionsFieldOrigin = \case
|
||||||
@ -202,7 +205,7 @@ data Projection
|
|||||||
| ArrayEntityProjection EntityAlias (Aliased [FieldName])
|
| ArrayEntityProjection EntityAlias (Aliased [FieldName])
|
||||||
| WindowProjection (Aliased WindowFunction)
|
| WindowProjection (Aliased WindowFunction)
|
||||||
deriving stock (Eq, Show, Generic, Data, Lift, Ord)
|
deriving stock (Eq, Show, Generic, Data, Lift, Ord)
|
||||||
deriving anyclass (FromJSON, Hashable, NFData)
|
deriving anyclass (Hashable, NFData)
|
||||||
|
|
||||||
data WindowFunction
|
data WindowFunction
|
||||||
= -- | ROW_NUMBER() OVER(PARTITION BY field)
|
= -- | ROW_NUMBER() OVER(PARTITION BY field)
|
||||||
@ -220,7 +223,7 @@ data Join = Join
|
|||||||
joinRightTable :: EntityAlias
|
joinRightTable :: EntityAlias
|
||||||
}
|
}
|
||||||
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
||||||
deriving anyclass (FromJSON, Hashable, NFData)
|
deriving anyclass (Hashable, NFData)
|
||||||
|
|
||||||
data JoinProvenance
|
data JoinProvenance
|
||||||
= OrderByJoinProvenance
|
= OrderByJoinProvenance
|
||||||
@ -229,19 +232,19 @@ data JoinProvenance
|
|||||||
| ArrayJoinProvenance [Text]
|
| ArrayJoinProvenance [Text]
|
||||||
| MultiplexProvenance
|
| MultiplexProvenance
|
||||||
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
||||||
deriving anyclass (FromJSON, Hashable, NFData)
|
deriving anyclass (Hashable, NFData)
|
||||||
|
|
||||||
data JoinSource
|
data JoinSource
|
||||||
= JoinSelect Select
|
= JoinSelect Select
|
||||||
-- We're not using existingJoins at the moment, which was used to
|
-- We're not using existingJoins at the moment, which was used to
|
||||||
-- avoid re-joining on the same table twice.
|
-- avoid re-joining on the same table twice.
|
||||||
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
||||||
deriving anyclass (FromJSON, Hashable, NFData)
|
deriving anyclass (Hashable, NFData)
|
||||||
|
|
||||||
newtype Where
|
newtype Where
|
||||||
= Where [Expression]
|
= Where [Expression]
|
||||||
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
||||||
deriving newtype (FromJSON, Hashable, Monoid, NFData, Semigroup)
|
deriving newtype (Hashable, Monoid, NFData, Semigroup)
|
||||||
|
|
||||||
data Cardinality
|
data Cardinality
|
||||||
= Many
|
= Many
|
||||||
@ -255,6 +258,11 @@ data AsStruct
|
|||||||
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
||||||
deriving anyclass (FromJSON, Hashable, NFData, ToJSON)
|
deriving anyclass (FromJSON, Hashable, NFData, ToJSON)
|
||||||
|
|
||||||
|
-- | A Common Table Expression clause.
|
||||||
|
newtype With = With (NonEmpty (Aliased (InterpolatedQuery Expression)))
|
||||||
|
deriving stock (Data, Generic, Lift)
|
||||||
|
deriving newtype (Eq, Hashable, NFData, Ord, Semigroup, Show)
|
||||||
|
|
||||||
data Top
|
data Top
|
||||||
= NoTop
|
= NoTop
|
||||||
| Top Int.Int64
|
| Top Int.Int64
|
||||||
@ -300,7 +308,7 @@ data Expression
|
|||||||
-- `argument_name` => 'argument_value'
|
-- `argument_name` => 'argument_value'
|
||||||
FunctionNamedArgument Text Expression
|
FunctionNamedArgument Text Expression
|
||||||
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
||||||
deriving anyclass (FromJSON, Hashable, NFData)
|
deriving anyclass (Hashable, NFData)
|
||||||
|
|
||||||
data JsonPath
|
data JsonPath
|
||||||
= RootPath
|
= RootPath
|
||||||
@ -315,7 +323,7 @@ data Aggregate
|
|||||||
| OpAggregate Text Expression
|
| OpAggregate Text Expression
|
||||||
| TextAggregate Text
|
| TextAggregate Text
|
||||||
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
||||||
deriving anyclass (FromJSON, Hashable, NFData)
|
deriving anyclass (Hashable, NFData)
|
||||||
|
|
||||||
data Countable fieldname
|
data Countable fieldname
|
||||||
= StarCountable
|
= StarCountable
|
||||||
@ -336,29 +344,30 @@ data From
|
|||||||
| FromSelect (Aliased Select)
|
| FromSelect (Aliased Select)
|
||||||
| FromSelectJson (Aliased SelectJson)
|
| FromSelectJson (Aliased SelectJson)
|
||||||
| FromFunction (Aliased SelectFromFunction)
|
| FromFunction (Aliased SelectFromFunction)
|
||||||
|
| FromLogicalModel LogicalModelName
|
||||||
deriving stock (Eq, Show, Generic, Data, Lift, Ord)
|
deriving stock (Eq, Show, Generic, Data, Lift, Ord)
|
||||||
deriving anyclass (FromJSON, Hashable, NFData)
|
deriving anyclass (Hashable, NFData)
|
||||||
|
|
||||||
data SelectJson = SelectJson
|
data SelectJson = SelectJson
|
||||||
{ selectJsonBody :: Expression,
|
{ selectJsonBody :: Expression,
|
||||||
selectJsonFields :: [(ColumnName, ScalarType)]
|
selectJsonFields :: [(ColumnName, ScalarType)]
|
||||||
}
|
}
|
||||||
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
||||||
deriving anyclass (FromJSON, Hashable, NFData)
|
deriving anyclass (Hashable, NFData)
|
||||||
|
|
||||||
data SelectFromFunction = SelectFromFunction
|
data SelectFromFunction = SelectFromFunction
|
||||||
{ sffFunctionName :: FunctionName,
|
{ sffFunctionName :: FunctionName,
|
||||||
sffArguments :: [Expression]
|
sffArguments :: [Expression]
|
||||||
}
|
}
|
||||||
deriving stock (Eq, Show, Generic, Data, Lift, Ord)
|
deriving stock (Eq, Show, Generic, Data, Lift, Ord)
|
||||||
deriving anyclass (FromJSON, Hashable, NFData)
|
deriving anyclass (Hashable, NFData)
|
||||||
|
|
||||||
data OpenJson = OpenJson
|
data OpenJson = OpenJson
|
||||||
{ openJsonExpression :: Expression,
|
{ openJsonExpression :: Expression,
|
||||||
openJsonWith :: NonEmpty JsonFieldSpec
|
openJsonWith :: NonEmpty JsonFieldSpec
|
||||||
}
|
}
|
||||||
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
deriving stock (Eq, Ord, Show, Generic, Data, Lift)
|
||||||
deriving anyclass (FromJSON, Hashable, NFData)
|
deriving anyclass (Hashable, NFData)
|
||||||
|
|
||||||
data JsonFieldSpec
|
data JsonFieldSpec
|
||||||
= IntField Text
|
= IntField Text
|
||||||
|
@ -39,6 +39,7 @@ import Hasura.RQL.Types.Common (SourceName, ToAesonPairs (toAesonPairs), default
|
|||||||
import Hasura.RQL.Types.Permission (SelPermDef, _pdRole)
|
import Hasura.RQL.Types.Permission (SelPermDef, _pdRole)
|
||||||
import Hasura.SQL.Backend
|
import Hasura.SQL.Backend
|
||||||
import Hasura.Session (RoleName)
|
import Hasura.Session (RoleName)
|
||||||
|
import Language.Haskell.TH.Syntax (Lift)
|
||||||
|
|
||||||
newtype RawQuery = RawQuery {getRawQuery :: Text}
|
newtype RawQuery = RawQuery {getRawQuery :: Text}
|
||||||
deriving newtype (Eq, Ord, Show, FromJSON, ToJSON)
|
deriving newtype (Eq, Ord, Show, FromJSON, ToJSON)
|
||||||
@ -54,7 +55,7 @@ data InterpolatedItem variable
|
|||||||
IIText Text
|
IIText Text
|
||||||
| -- | a captured variable
|
| -- | a captured variable
|
||||||
IIVariable variable
|
IIVariable variable
|
||||||
deriving stock (Eq, Ord, Show, Functor, Foldable, Data, Generic, Traversable)
|
deriving stock (Eq, Ord, Show, Functor, Foldable, Data, Generic, Lift, Traversable)
|
||||||
|
|
||||||
-- | Converting an interpolated query back to text.
|
-- | Converting an interpolated query back to text.
|
||||||
-- Should roundtrip with the 'parseInterpolatedQuery'.
|
-- Should roundtrip with the 'parseInterpolatedQuery'.
|
||||||
@ -74,7 +75,7 @@ newtype InterpolatedQuery variable = InterpolatedQuery
|
|||||||
{ getInterpolatedQuery :: [InterpolatedItem variable]
|
{ getInterpolatedQuery :: [InterpolatedItem variable]
|
||||||
}
|
}
|
||||||
deriving newtype (Eq, Ord, Show, Generic)
|
deriving newtype (Eq, Ord, Show, Generic)
|
||||||
deriving stock (Data, Functor, Foldable, Traversable)
|
deriving stock (Data, Functor, Foldable, Lift, Traversable)
|
||||||
|
|
||||||
deriving newtype instance (Hashable variable) => Hashable (InterpolatedQuery variable)
|
deriving newtype instance (Hashable variable) => Hashable (InterpolatedQuery variable)
|
||||||
|
|
||||||
|
@ -15,11 +15,12 @@ import Hasura.Metadata.DTO.Utils (codecNamePrefix)
|
|||||||
import Hasura.Prelude hiding (first)
|
import Hasura.Prelude hiding (first)
|
||||||
import Hasura.RQL.Types.Backend (Backend (..))
|
import Hasura.RQL.Types.Backend (Backend (..))
|
||||||
import Language.GraphQL.Draft.Syntax qualified as G
|
import Language.GraphQL.Draft.Syntax qualified as G
|
||||||
|
import Language.Haskell.TH.Syntax (Lift)
|
||||||
|
|
||||||
-- The name of a logical model. This appears as a root field name in the graphql schema.
|
-- The name of a logical model. This appears as a root field name in the graphql schema.
|
||||||
newtype LogicalModelName = LogicalModelName {getLogicalModelName :: G.Name}
|
newtype LogicalModelName = LogicalModelName {getLogicalModelName :: G.Name}
|
||||||
deriving newtype (Eq, Ord, Show, Hashable, NFData, ToJSON, FromJSON, ToTxt)
|
deriving newtype (Eq, Ord, Show, Hashable, NFData, ToJSON, FromJSON, ToTxt)
|
||||||
deriving stock (Generic)
|
deriving stock (Data, Generic, Lift)
|
||||||
|
|
||||||
instance HasCodec LogicalModelName where
|
instance HasCodec LogicalModelName where
|
||||||
codec = dimapCodec LogicalModelName getLogicalModelName codec
|
codec = dimapCodec LogicalModelName getLogicalModelName codec
|
||||||
|
Loading…
Reference in New Issue
Block a user