diff --git a/connector/connector.go b/connector/connector.go index 5ecdb5b..8383897 100644 --- a/connector/connector.go +++ b/connector/connector.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "net/http" + "path/filepath" "github.com/hasura/ndc-http/connector/internal" "github.com/hasura/ndc-http/ndc-http-schema/configuration" @@ -68,6 +69,7 @@ func (c *HTTPConnector) ParseConfiguration(ctx context.Context, configurationDir var errs map[string][]string if schemas == nil { + logger.Debug(fmt.Sprintf("output file at %s does not exist. Parsing files...", filepath.Join(configurationDir, config.Output))) schemas, errs = configuration.BuildSchemaFromConfig(config, configurationDir, logger) if len(errs) > 0 { printSchemaValidationError(logger, errs) diff --git a/ndc-http-schema/configuration/argument_presets.go b/ndc-http-schema/configuration/argument_presets.go index 22a4b15..7cace6d 100644 --- a/ndc-http-schema/configuration/argument_presets.go +++ b/ndc-http-schema/configuration/argument_presets.go @@ -135,7 +135,11 @@ func evalArgumentFromJSONPath(httpSchema *rest.NDCHttpSchema, typeSchema schema. selectorName, ok := segments[0].Selectors()[0].(spec.Name) if !ok || selectorName == "" { - return nil, nil, errors.New("invalid json path: " + segments[0].String()) + return nil, nil, errors.New("unsupported json path selector: " + segments[0].String()) + } + + if selectorName == "" { + return nil, nil, errors.New("invalid json path, empty selector name: " + segments[0].String()) } selector := string(selectorName) diff --git a/ndc-http-schema/configuration/schema.go b/ndc-http-schema/configuration/schema.go index 1770e31..c616362 100644 --- a/ndc-http-schema/configuration/schema.go +++ b/ndc-http-schema/configuration/schema.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "reflect" + "slices" "strconv" rest "github.com/hasura/ndc-http/ndc-http-schema/schema" @@ -19,6 +20,8 @@ import ( func BuildSchemaFromConfig(config *Configuration, configDir string, logger *slog.Logger) ([]NDCHttpRuntimeSchema, map[string][]string) { schemas := make([]NDCHttpRuntimeSchema, len(config.Files)) errors := make(map[string][]string) + existedFileIDs := []string{} + for i, file := range config.Files { schemaOutput, err := buildSchemaFile(config, configDir, &file, logger) if err != nil { @@ -28,18 +31,25 @@ func BuildSchemaFromConfig(config *Configuration, configDir string, logger *slog if schemaOutput == nil { continue } + fileID := file.File + if slices.Contains(existedFileIDs, fileID) { + logger.Warn(fmt.Sprintf("the file %s is duplicated. Make sure that is intended", fileID)) + fileID += "_" + strconv.Itoa(i) + } + ndcSchema := NDCHttpRuntimeSchema{ - Name: file.File, + Name: fileID, NDCHttpSchema: schemaOutput, } runtime, err := file.GetRuntimeSettings() if err != nil { - errors[file.File] = []string{err.Error()} + errors[fileID] = []string{err.Error()} } else { ndcSchema.Runtime = *runtime } + existedFileIDs = append(existedFileIDs, fileID) schemas[i] = ndcSchema } diff --git a/ndc-http-schema/configuration/templates/env_variables.gotmpl b/ndc-http-schema/configuration/templates/env_variables.gotmpl index 7f2dfd1..54f1f67 100644 --- a/ndc-http-schema/configuration/templates/env_variables.gotmpl +++ b/ndc-http-schema/configuration/templates/env_variables.gotmpl @@ -10,7 +10,7 @@ ``` {{ .ContextPath }}/connector.yaml envMapping: - {{range $index, $variable := .Variables }}{{ index $variable 0 }} + {{range $index, $variable := .Variables }}{{ index $variable 0 }}: fromEnv: {{ index $variable 1 }} {{end}}# ... ``` diff --git a/ndc-http-schema/configuration/testdata/validation/connector/http/schema.yaml b/ndc-http-schema/configuration/testdata/validation/connector/http/schema.yaml index 88eebdd..86cca98 100644 --- a/ndc-http-schema/configuration/testdata/validation/connector/http/schema.yaml +++ b/ndc-http-schema/configuration/testdata/validation/connector/http/schema.yaml @@ -61,6 +61,12 @@ settings: value: 1 targets: - addPet + - path: status + value: + type: forwardHeader + name: X-Pet-Status + targets: + - findPetsByStatus tls: # Path to the TLS cert to use for TLS required connections. certFile: diff --git a/ndc-http-schema/configuration/testdata/validation/expected.tpl b/ndc-http-schema/configuration/testdata/validation/expected.tpl index ec3b1ba..39d4d6d 100644 --- a/ndc-http-schema/configuration/testdata/validation/expected.tpl +++ b/ndc-http-schema/configuration/testdata/validation/expected.tpl @@ -1,8 +1,12 @@ WARNING: - * Headers forwarding should be enabled for the following authentication schemes: [cookie openIdConnect] + * Authorization header must be forwarded for the following authentication schemes: [cookie openIdConnect] See https://github.com/hasura/ndc-http/blob/main/docs/authentication.md#headers-forwarding for more information. + testdata/validation/connector/http/schema.yaml + + * Make sure that the X-Pet-Status header is added to the header forwarding list. + Environment Variables: Make sure that the following environment variables were added to your subgraph configuration: @@ -31,35 +35,35 @@ Environment Variables: ``` testdata/validation/connector/http/connector.yaml envMapping: - CAT_PET_HEADER + CAT_PET_HEADER: fromEnv: APP_MYAPI_CAT_PET_HEADER - CAT_STORE_CA_PEM + CAT_STORE_CA_PEM: fromEnv: APP_MYAPI_CAT_STORE_CA_PEM - CAT_STORE_CERT_PEM + CAT_STORE_CERT_PEM: fromEnv: APP_MYAPI_CAT_STORE_CERT_PEM - CAT_STORE_KEY_PEM + CAT_STORE_KEY_PEM: fromEnv: APP_MYAPI_CAT_STORE_KEY_PEM - CAT_STORE_URL + CAT_STORE_URL: fromEnv: APP_MYAPI_CAT_STORE_URL - DEFAULT_PET_NAME + DEFAULT_PET_NAME: fromEnv: APP_MYAPI_DEFAULT_PET_NAME - OAUTH2_CLIENT_ID + OAUTH2_CLIENT_ID: fromEnv: APP_MYAPI_OAUTH2_CLIENT_ID - OAUTH2_CLIENT_SECRET + OAUTH2_CLIENT_SECRET: fromEnv: APP_MYAPI_OAUTH2_CLIENT_SECRET - PET_STORE_API_KEY + PET_STORE_API_KEY: fromEnv: APP_MYAPI_PET_STORE_API_KEY - PET_STORE_BEARER_TOKEN + PET_STORE_BEARER_TOKEN: fromEnv: APP_MYAPI_PET_STORE_BEARER_TOKEN - PET_STORE_CA_PEM + PET_STORE_CA_PEM: fromEnv: APP_MYAPI_PET_STORE_CA_PEM - PET_STORE_CERT_PEM + PET_STORE_CERT_PEM: fromEnv: APP_MYAPI_PET_STORE_CERT_PEM - PET_STORE_KEY_PEM + PET_STORE_KEY_PEM: fromEnv: APP_MYAPI_PET_STORE_KEY_PEM - PET_STORE_TEST_HEADER + PET_STORE_TEST_HEADER: fromEnv: APP_MYAPI_PET_STORE_TEST_HEADER - PET_STORE_URL + PET_STORE_URL: fromEnv: APP_MYAPI_PET_STORE_URL # ... diff --git a/ndc-http-schema/configuration/validate.go b/ndc-http-schema/configuration/validate.go index 36cc5f3..5765eb9 100644 --- a/ndc-http-schema/configuration/validate.go +++ b/ndc-http-schema/configuration/validate.go @@ -89,15 +89,16 @@ func (cv *ConfigValidator) Render(w io.Writer) { if len(cv.warnings) > 0 || len(cv.requiredHeadersForwarding) > 0 { writeWarningIf(w, ":\n", cv.noColor) if len(cv.requiredHeadersForwarding) > 0 && (!cv.config.ForwardHeaders.Enabled || cv.config.ForwardHeaders.ArgumentField == nil || *cv.config.ForwardHeaders.ArgumentField == "") { - _, _ = w.Write([]byte(fmt.Sprintf("\n * Headers forwarding should be enabled for the following authentication schemes: %v", utils.GetSortedKeys(cv.requiredHeadersForwarding)))) + _, _ = w.Write([]byte(fmt.Sprintf("\n * Authorization header must be forwarded for the following authentication schemes: %v", utils.GetSortedKeys(cv.requiredHeadersForwarding)))) _, _ = w.Write([]byte("\n See https://github.com/hasura/ndc-http/blob/main/docs/authentication.md#headers-forwarding for more information.")) } for ns, errs := range cv.warnings { - _, _ = w.Write([]byte("\n\n")) + _, _ = w.Write([]byte("\n\n ")) _, _ = w.Write([]byte(ns)) + _, _ = w.Write([]byte("\n")) for _, err := range errs { - _, _ = w.Write([]byte("\n * ")) + _, _ = w.Write([]byte("\n * ")) _, _ = w.Write([]byte(err)) } } @@ -221,10 +222,13 @@ func (cv *ConfigValidator) validateArgumentPresets(namespace string, key string, continue } - if env, ok := preset.Value.Interface().(*schema.ArgumentPresetValueEnv); ok { - if _, envOk := os.LookupEnv(env.Name); !envOk { - cv.requiredVariables[env.Name] = true + switch t := preset.Value.Interface().(type) { + case *schema.ArgumentPresetValueEnv: + if _, envOk := os.LookupEnv(t.Name); !envOk { + cv.requiredVariables[t.Name] = true } + case *schema.ArgumentPresetValueForwardHeader: + cv.addWarning(namespace, fmt.Sprintf("Make sure that the %s header is added to the header forwarding list.", t.Name)) } } }