Merge branch 'master' into feature/add-identity-frequent-column

This commit is contained in:
Aleksandra Sikora 2020-08-06 12:11:46 +02:00 committed by GitHub
commit bf5a416f3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 159 additions and 28 deletions

View File

@ -2,14 +2,25 @@
## Next release
### Breaking change
Headers from environment variables starting with `HASURA_GRAPHQL_` are not allowed
in event triggers, actions & remote schemas.
If you do have such headers configured, then you must update the header configuration before upgrading.
### Bug fixes and improvements
(Add entries here in the order of: server, console, cli, docs, others)
- server: fix failing introspection query when an enum column is part of a primary key (fixes #5200)
- server: disallow headers from env variables starting with `HASURA_GRAPHQL_` in actions, event triggers & remote schemas (#5519)
**WARNING**: This might break certain deployments. See `Breaking change` section above.
- server: bugfix to allow HASURA_GRAPHQL_QUERY_PLAN_CACHE_SIZE of 0 (#5363)
- server: support only a bounded plan cache, with a default size of 4000 (closes #5363)
- server: add logs for action handlers
- server: add request/response sizes in event triggers (and scheduled trigger) logs
- server: add request/response sizes in event triggers (and scheduled trigger) logs (#5463)
- server: change startup log kind `db_migrate` to `catalog_migrate` (#5531)
- console: handle nested fragments in allowed queries (close #5137) (#5252)
- console: update sidebar icons for different action and trigger types (#5445)
- console: make add column UX consistent with others (#5486)

View File

@ -40,7 +40,7 @@ const executeSQL = (isMigration, migrationName, statementTimeout) => (
dispatch({ type: MAKING_REQUEST });
dispatch(showSuccessNotification('Executing the Query...'));
const { isTableTrackChecked, isCascadeChecked } = getState().rawSQL;
const { isTableTrackChecked, isCascadeChecked, sql } = getState().rawSQL;
const { migrationMode, readOnlyMode } = getState().main;
const migrateUrl = returnMigrateUrl(migrationMode);
@ -49,13 +49,17 @@ const executeSQL = (isMigration, migrationName, statementTimeout) => (
const schemaChangesUp = [];
const sql =
statementTimeout && !isMigration
? `${getStatementTimeoutSql(statementTimeout)} ${getState().rawSQL.sql}`
: getState().rawSQL.sql;
if (statementTimeout && !isMigration) {
schemaChangesUp.push(
getRunSqlQuery(
getStatementTimeoutSql(statementTimeout),
false,
readOnlyMode
)
);
}
schemaChangesUp.push(getRunSqlQuery(sql, isCascadeChecked, readOnlyMode));
// check if track view enabled
if (isTableTrackChecked) {
const objects = parseCreateSQL(sql);

View File

@ -774,7 +774,7 @@ class PermissionBuilder extends React.Component {
const _tableExp = [
{ key: 'schema', value: schemaSelect },
{ key: 'table', value: tableSelect },
{ key: 'name', value: tableSelect },
];
return <QueryBuilderJson element={_tableExp} />;

View File

@ -242,7 +242,7 @@ migrateCatalogSchema env logger pool httpManager sqlGenCtx = do
initialiseResult `onLeft` \err -> do
unLogger logger StartupLog
{ slLogLevel = LevelError
, slKind = "db_migrate"
, slKind = "catalog_migrate"
, slInfo = A.toJSON err
}
liftIO (printErrExit DatabaseMigrationError (BLC.unpack $ A.encode err))

View File

@ -214,6 +214,7 @@ mkMutationTypesAndFieldsRole tn insPermM selFldsM updColsM delPermM pkeyCols con
(selFldsM ^.. _Just.traverse._SFPGColumn)
<> (insPermM ^. _Just._1)
<> (updColsM ^. _Just)
<> (pkeyCols ^. _Just.pkColumns.to toList)
allEnumReferences = allColumnInfos ^.. traverse.to pgiType._PGColumnEnumReference
in flip map allEnumReferences $ \enumReference@(EnumReference referencedTableName _) ->
let typeName = mkTableEnumType referencedTableName

View File

@ -8,8 +8,8 @@ import Hasura.RQL.Types.Error
import Language.Haskell.TH.Syntax (Lift)
import qualified Data.CaseInsensitive as CI
import qualified Data.Text as T
import qualified Data.Environment as Env
import qualified Data.Text as T
import qualified Network.HTTP.Types as HTTP
@ -35,7 +35,10 @@ instance FromJSON HeaderConf where
case (value, valueFromEnv ) of
(Nothing, Nothing) -> fail "expecting value or value_from_env keys"
(Just val, Nothing) -> return $ HeaderConf name (HVValue val)
(Nothing, Just val) -> return $ HeaderConf name (HVEnv val)
(Nothing, Just val) -> do
when (T.isPrefixOf "HASURA_GRAPHQL_" val) $
fail $ "env variables starting with \"HASURA_GRAPHQL_\" are not allowed in value_from_env: " <> T.unpack val
return $ HeaderConf name (HVEnv val)
(Just _, Just _) -> fail "expecting only one of value or value_from_env keys"
parseJSON _ = fail "expecting object for headers"

View File

@ -64,7 +64,7 @@ data MigrationResult
instance ToEngineLog MigrationResult Hasura where
toEngineLog result = toEngineLog $ StartupLog
{ slLogLevel = LevelInfo
, slKind = "db_migrate"
, slKind = "catalog_migrate"
, slInfo = A.toJSON $ case result of
MRNothingToDo ->
"Already at the latest catalog version (" <> latestCatalogVersionString

View File

@ -26,6 +26,7 @@ import Control.Monad.Trans.Control
import Control.Monad.Morph
import Control.Monad.Unique
import Data.String (fromString)
import Network.URI (URI)
import qualified Data.Aeson as J
import qualified Data.Aeson.Lens as JL
@ -237,22 +238,25 @@ extractEventContext e = do
-- | Perform HTTP request which supports Trace headers
tracedHttpRequest
:: MonadTrace m
=> HTTP.Request
=> HTTP.Request
-- ^ http request that needs to be made
-> (HTTP.Request -> m a)
-- ^ a function that takes the traced request and executes it
-> m a
tracedHttpRequest req f = trace (bsToTxt (HTTP.path req)) do
let reqBytes = case HTTP.requestBody req of
HTTP.RequestBodyBS bs -> Just (fromIntegral (BS.length bs))
HTTP.RequestBodyLBS bs -> Just (BL.length bs)
HTTP.RequestBodyBuilder len _ -> Just len
HTTP.RequestBodyStream len _ -> Just len
_ -> Nothing
for_ reqBytes \b ->
attachMetadata [("request_body_bytes", fromString (show b))]
ctx <- currentContext
let req' = req { HTTP.requestHeaders =
injectHttpContext ctx <> HTTP.requestHeaders req
}
f req'
tracedHttpRequest req f = do
let method = bsToTxt (HTTP.method req)
uri = show @URI (HTTP.getUri req)
trace (method <> " " <> fromString uri) do
let reqBytes = case HTTP.requestBody req of
HTTP.RequestBodyBS bs -> Just (fromIntegral (BS.length bs))
HTTP.RequestBodyLBS bs -> Just (BL.length bs)
HTTP.RequestBodyBuilder len _ -> Just len
HTTP.RequestBodyStream len _ -> Just len
_ -> Nothing
for_ reqBytes \b ->
attachMetadata [("request_body_bytes", fromString (show b))]
ctx <- currentContext
let req' = req { HTTP.requestHeaders =
injectHttpContext ctx <> HTTP.requestHeaders req
}
f req'

View File

@ -0,0 +1,21 @@
description: Define an action with headers configuration
url: /v1/query
status: 400
query:
type: create_action
args:
name: create_user_1
definition:
kind: synchronous
arguments:
- name: name
type: String!
output_type: User!
handler: http://127.0.0.1:5593/create-user
headers:
- name: x-client-id
value_from_env: HASURA_GRAPHQL_CLIENT_NAME
response:
path: $.definition.headers[0]
error: 'env variables starting with "HASURA_GRAPHQL_" are not allowed in value_from_env: HASURA_GRAPHQL_CLIENT_NAME'
code: parse-failed

View File

@ -0,0 +1,48 @@
# https://github.com/hasura/graphql-engine/issues/5200
description: Test introspecting enum types as user role
url: /v1/graphql
status: 200
headers:
X-Hasura-Role: user
response:
data:
country:
kind: ENUM
name: country_enum
enumValues:
- name: India
description: Republic of India
- name: USA
description: United States of America
zones:
fields:
- name: code
type:
ofType:
name: String
- name: id
type:
ofType:
name: Int
query:
query: |
{
country: __type(name: "country_enum") {
name
kind
enumValues {
name
description
}
}
zones: __type(name: "zones") {
fields {
name
type {
ofType {
name
}
}
}
}
}

View File

@ -23,12 +23,34 @@ args:
('Alyssa', 'red'),
('Ben', 'blue');
CREATE TABLE country
( value text PRIMARY KEY
, comment text);
INSERT INTO country (value, comment) VALUES
('India', 'Republic of India'),
('USA', 'United States of America');
CREATE TABLE zones
( id SERIAL
, code text NOT NULL
, country text NOT NULL REFERENCES country
, PRIMARY KEY (code, country) );
INSERT INTO zones (code, country) VALUES
('67432', 'USA'),
('600036', 'India');
- type: track_table
args:
table: colors
is_enum: true
- type: track_table
args: users
- type: track_table
args:
table: country
is_enum: true
- type: track_table
args: zones
# Anonymous users can query users, but not colors
- type: create_select_permission
@ -38,3 +60,12 @@ args:
permission:
columns: [id, name, favorite_color]
filter: {}
# A user can query only code but not country
- type: create_select_permission
args:
table: zones
role: user
permission:
columns: [id, code]
filter: {}

View File

@ -5,4 +5,6 @@ args:
sql: |
DROP TABLE users;
DROP TABLE colors;
DROP TABLE zones;
DROP TABLE country;
cascade: true

View File

@ -455,6 +455,9 @@ class TestActionsMetadata:
def test_recreate_permission(self, hge_ctx):
check_query_f(hge_ctx, self.dir() + '/recreate_permission.yaml')
def test_create_with_headers(self, hge_ctx):
check_query_f(hge_ctx, self.dir() + '/create_with_headers.yaml')
# Test case for bug reported at https://github.com/hasura/graphql-engine/issues/5166
@pytest.mark.usefixtures('per_class_tests_db_state')
class TestActionIntrospection:

View File

@ -561,6 +561,9 @@ class TestGraphQLQueryEnums:
def test_introspect(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/introspect.yaml', transport)
def test_introspect_user_role(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/introspect_user_role.yaml', transport)
def test_select_enum_field(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/select_enum_field.yaml', transport)