Skip to content

Latest commit

 

History

History
285 lines (242 loc) · 8.53 KB

config_schema_guidelines.md

File metadata and controls

285 lines (242 loc) · 8.53 KB

JSON schema guidelines

These guidelines apply to JSON schemas that are associated with endpoint and resource configurations for connectors. The purpose of this is to help us get consistent and robust UI forms, which are generated from these schemas.

Everything should have a title and a description

The title is rendered as the field label in the UI. Everything should have a sensible label. The description is also rendered next to each field, and should generally be present for most fields. Both title and description are pulled into the documentation, as well.

Note that description must not contain any inline HTML, as it will not be rendered.

Additional things to keep in mind when writing descriptions:

  • Punctuation: Descriptions should always end with a period, even when they're just sentence fragments.
  • Capitalization: While titles should be in Title Case, descriptions are capitalized like ordinary sentences.
  • Links: Always use an Estuary branded shortlink for doc links in descriptions. Whenever possible, link to a relevant section in the Estuary docs rather than third-party docs.
  • Voice: Use second person; refer to the reader as "you." Don't refer to "users," "developers," etc in descriptions.

Group related fields within an object

Related fields should be grouped together in a separate JSON object, which has a title and a description. This helps things render nicely so that the related fields are close together. For example, an AWS access key and secret key should always be next to each other in the UI since they always need to be provided together.

Example:

{
    "type": "object",
    "title": "My Connector Config",
    "properties": {
        "awsCredentials": {
            "type": "object",
            "title": "AWS Credentials",
            "properties": {
                "accessKey": {
                    "type": "string",
                    "title": "AWS Access Key ID"
                },
                "secretKey": {
                    "type": "string",
                    "title": "AWS Secret Access Key"
                }
            }
        },
        "otherStuff": {...}
    }
}

Always allow additional properties in endpoint configurations

The root schemas should always allow additional properties. This is the default, so you don't need to add anything explicitly. Just don't use "additionalProperties": false. This is because the sops encryption will add a sops field at the root of the endpoint configuration, which will of course cause validation to fail if your schema disallows it.

Booleans should have a default

Boolean properties get rendered as a check box in the UI. Having a default value helps to resolve the ambiguity when a user checks and then unchecks the box. Without the default, the value will begin as undefined, and then after the user checks and unchecks the box, it will be false. It's then ambiguous as to whether the connector will treat undefined and false equivalently. Therefore, it seems best to always provide an explicit default, so that boolean checkboxes never have undefined values.

Example:

{
  "type": "boolean",
  "title": "Do the thing",
  "description": "whether or not to do the thing",
  "default": false
}

Use secret annotation for all sensitive fields

Any kind of credentials or secrets need to be encrypted. We use the secret annotation to determine which specific fields should be encrypted, and we leave the rest as plain text so that they can be directly edited in the UI. Technically the airbyte_secret annotation will also work for this, but we of course prefer plain secret where possible.

Example:

{
    "title": "My connector config",
    "type": "object",
    "properties": {
        "apiKey": {
            "type": "string",
            "title": "Super Secret Software Stuff"
            "secret": true
        },
        "plainProperty": {
            "type": "string",
            "title": "Stuff that stays in plain text"
        }
    }
}

Use advanced annotation for objects that should be collapsed by default

Some configuration tends to be used only in certain less common scenarios. Network tunneling is a good example of this, as it's something that most users would want to ignore in most cases. Setting "advanced": true in a schema with "type": "object" will cause the form for that object to be collapsed by default. Users can still see the title, but they'll need to click on it to show all the fields. This help to avoid overwhelming users with a bunch of fields that aren't relevant to what they're doing.

Example:

{
  "type": "object",
  "title": "My connector config",
  "properties": {
    "commonFields": {
      "title": "Common fields that most users need to consider",
      "type": "object",
      "properties": {
        "commonFieldA": {
          "type": "string",
          "title": "Some Common Config"
        },
        "commonFieldB": {
          "type": "integer",
          "title": "Common int field"
        }
      }
    },
    "uncommonFields": {
      "title": "Uncommon fields that might be overwhelming to users",
      "advanced": true,
      "type": "object",
      "properties": {
        "uncommonFieldA": {
          "type": "string",
          "title": "Some Uncommon Config"
        },
        "uncommonFieldB": {
          "type": "integer",
          "title": "Uncommon int field"
        }
      }
    }
  }
}

Use multiline annotation for long strings

If a string field is allowed to contain newline characters, then you need to set "multiline": true in order for the input to allow them. This will also cause the input to dynamically expand as lines of text are added, so that the entire value can be shown.

Example:

{
  "type": "string",
  "title": "Some long string value",
  "multiline": true
}

Include type when using enum

Anytime you are using an enum in your schema you will need to include a type for it. This is for either simple cases (example 1) and for nested ones like example 2.

Example 1:

{
  "type": "string",
  "enum": ["foo", "bar", "baz"],
  "title": "A single selection field"
}

Example 1:

{
  "type": "array",
  "items": {
    "enum": ["foo", "bar", "buz"],
    "type": "string",
    "title": "Possible Choices",
    "description": "These are your choices"
  },
  "title": "Fields",
  "default": [],
  "description": "A list of selectable properties"
}

Use discriminator to differenciate complex oneOf

Anytime you have a oneOf that contains several options that do not have matching schemas you need to include the discriminator property. This property needs to contain a const and default value and that value must be unique.

This is mainly for the UI and the validator used in the UI has some limitations. AJV Docs

To see a working example in production reference parser in Amazon S3

Example 1:

{
  "properties": {
    "setting_name_foo": {
      "discriminator": {
        "propertyName": "foo_bar"
      },
      "oneOf": [
        {
          "properties": {
            "foo_bar": {
              "const": "unique_value_one",
              "default": "unique_value_one",
              "type": "string"
            },
            "prop_fizz": {...},
            "prop_buzz": {...}
          },
          "required": [
            "foo_bar",
            "prop_fizz",
            "prop_buzz"
          ],
          ...
        },
        {
          "properties": {
            "foo_bar": {
              "const": "unique_value_two",
              "default": "unique_value_two",
              "type": "string"
            },
            "prop_foo": {...},
            "prop_bar": {...}
          },
          "required": [
            "foo_bar",
            "prop_foo",
            "prop_bar"
          ],
          ...
        }
      ],
      "title": "Authentication",
      "type": "object"
    },
    ...
  },
  "required": [
    "setting_name_foo",
    ...
  ],
  ...
}

Use sshEndpoint when you want the UI to show informational section about IP Address

The UI detects if a section contains the property sshEndpoint and then displays an information section telling the user what IP address they will need to allow through. It does not need to be required or any other special cases.

Keep in mind - it will display at the top of the group that contains the property and not directly next to the field.

{
  "properties": {
    "sshEndpoint": {
      ...
    },
  ...
}