Skip to content

Commit

Permalink
connectors-qa: add check for 'maxSecondsBetweenMessages' presence in …
Browse files Browse the repository at this point in the history
…certified connectors metadata (#36803)

Co-authored-by: Natik Gadzhi <natik@respawn.io>
  • Loading branch information
DanyloGL and natikgadzhi committed Apr 25, 2024
1 parent 3ca88e6 commit 17a374e
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 4 deletions.
4 changes: 4 additions & 0 deletions airbyte-ci/connectors/connectors_qa/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ poe lint
```
## Changelog

### 1.3.0

Added `CheckConnectorMaxSecondsBetweenMessagesValue` check that verifies presence of `maxSecondsBetweenMessages` value in `metadata.yaml` file for all source certified connectors.

### 1.2.0

Added `ValidateBreakingChangesDeadlines` check that verifies the minimal compliance of breaking change rollout deadline.
Expand Down
2 changes: 1 addition & 1 deletion airbyte-ci/connectors/connectors_qa/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "connectors-qa"
version = "1.2.0"
version = "1.3.0"
description = "A package to run QA checks on Airbyte connectors, generate reports and documentation."
authors = ["Airbyte <contact@airbyte.io>"]
readme = "README.md"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ def _run(self, connector: Connector) -> CheckResult:

class ValidateBreakingChangesDeadlines(MetadataCheck):
"""
Verify that _if_ the the most recent connector version has a breaking change,
Verify that _if_ the most recent connector version has a breaking change,
it's deadline is at least a week in the future.
"""

Expand Down Expand Up @@ -209,9 +209,29 @@ def _run(self, connector: Connector) -> CheckResult:
return self.pass_(connector=connector, message="The upgrade deadline is set to at least a week in the future")


class CheckConnectorMaxSecondsBetweenMessagesValue(MetadataCheck):
name = "Certified source connector must have a value filled out for maxSecondsBetweenMessages in metadata"
description = "Certified source connectors must have a value filled out for `maxSecondsBetweenMessages` in metadata. This value represents the maximum number of seconds we could expect between messages for API connectors. And it's used by platform to tune connectors heartbeat timeout. The value must be set in the 'data' field in connector's `metadata.yaml` file."
applies_to_connector_types = ["source"]
applies_to_connector_support_levels = ["certified"]

def _run(self, connector: Connector) -> CheckResult:
max_seconds_between_messages = connector.metadata.get("maxSecondsBetweenMessages")
if not max_seconds_between_messages:
return self.fail(
connector=connector,
message="Missing required for certified connectors field 'maxSecondsBetweenMessages'",
)
return self.pass_(
connector=connector,
message="Value for maxSecondsBetweenMessages is set",
)


ENABLED_CHECKS = [
ValidateMetadata(),
CheckConnectorLanguageTag(),
CheckConnectorCDKTag(),
ValidateBreakingChangesDeadlines(),
CheckConnectorMaxSecondsBetweenMessagesValue(),
]
16 changes: 15 additions & 1 deletion airbyte-ci/connectors/connectors_qa/src/connectors_qa/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from datetime import datetime
from enum import Enum
from pathlib import Path
from typing import Dict, List
from typing import Dict, List, Optional

from connector_ops.utils import Connector, ConnectorLanguage # type: ignore
from connectors_qa import consts
Expand Down Expand Up @@ -135,6 +135,15 @@ def category(self) -> CheckCategory:
"""
raise NotImplementedError("Subclasses must implement category property/attribute")

@property
def applies_to_connector_support_levels(self) -> Optional[List[str]]:
"""The connector's support levels that the QA check applies to
Returns:
List[str]: None if connector's support levels that the QA check applies to is not specified
"""
return None

def run(self, connector: Connector) -> CheckResult:
if not self.runs_on_released_connectors and connector.is_released:
return self.skip(
Expand All @@ -158,6 +167,11 @@ def run(self, connector: Connector) -> CheckResult:
connector,
f"Check does not apply to {connector.connector_type} connectors",
)
if self.applies_to_connector_support_levels and connector.support_level not in self.applies_to_connector_support_levels:
return self.skip(
connector,
f"Check does not apply to {connector.support_level} connectors",
)
return self._run(connector)

def _run(self, connector: Connector) -> CheckResult:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ They are by no mean replacing the need for a manual review of the connector code
### {{ check.name }}
*Applies to the following connector types: {{ ', '.join(check.applies_to_connector_types) }}*
*Applies to the following connector languages: {{ ', '.join(check.applies_to_connector_languages) }}*
*Applies to connector with {{ ', '.join(check.applies_to_connector_support_levels) if check.applies_to_connector_support_levels else 'any' }} support level*

{{ check.description }}
{%- endfor %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ def test_pass_when_java(self, mocker, tmp_path):
assert result.status == CheckStatus.PASSED
assert result.message == "Language tag language:java is present in the metadata file"


class TestCheckConnectorCDKTag:

def test_fail_when_no_cdk_tags(self, mocker):
Expand All @@ -179,7 +180,7 @@ def test_fail_when_multiple_cdk_tags(self, mocker):
# Assert
assert result.status == CheckStatus.FAILED
assert result.message == "Multiple CDK tags found in the metadata file: ['cdk:low-code', 'cdk:python']"

def test_fail_when_low_code_tag_on_python_connector(self, mocker, tmp_path):
# Arrange
connector = mocker.MagicMock(metadata={"tags": ["cdk:low-code"]}, code_directory=tmp_path)
Expand Down Expand Up @@ -208,3 +209,27 @@ def test_fail_when_python_tag_on_low_code_connector(self, mocker, tmp_path):
assert result.status == CheckStatus.FAILED
assert "Expected CDK tag 'cdk:low-code'" in result.message
assert "but found 'cdk:python'" in result.message


class TestCheckConnectorMaxSecondsBetweenMessagesValue:
def test_fail_when_field_missing(self, mocker):
# Arrange
connector = mocker.MagicMock(metadata={"supportLevel": "certified"})

# Act
result = metadata.CheckConnectorMaxSecondsBetweenMessagesValue()._run(connector)

# Assert
assert result.status == CheckStatus.FAILED
assert result.message == "Missing required for certified connectors field 'maxSecondsBetweenMessages'"

def test_pass_when_field_present(self, mocker):
# Arrange
connector = mocker.MagicMock(metadata={"supportLevel": "certified", "maxSecondsBetweenMessages": 1})

# Act
result = metadata.CheckConnectorMaxSecondsBetweenMessagesValue()._run(connector)

# Assert
assert result.status == CheckStatus.PASSED
assert result.message == "Value for maxSecondsBetweenMessages is set"
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,16 @@ def test_skip_when_check_does_not_apply_to_released_connectors(self, mocker):
# Assert
assert all(result.status == CheckStatus.SKIPPED for result in results)
assert all(result.message == "Check does not apply to released connectors" for result in results)

def test_skip_when_connector_support_level_does_not_apply_to(self, mocker):
# Arrange
connector = mocker.MagicMock(support_level="community")

# Act
results = []
for check in ENABLED_CHECKS:
if check.applies_to_connector_support_levels and connector.support_level not in check.applies_to_connector_support_levels:
results.append(check.run(connector))

# Assert
assert all(result.status == CheckStatus.SKIPPED for result in results)
22 changes: 22 additions & 0 deletions docs/contributing-to-airbyte/resources/qa-checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,20 @@ They are by no mean replacing the need for a manual review of the connector code
### Breaking changes must be accompanied by a migration guide
*Applies to the following connector types: source, destination*
*Applies to the following connector languages: java, low-code, python*
*Applies to connector with any support level*

When a breaking change is introduced, we check that a migration guide is available. It should be stored under `./docs/integrations/<connector-type>s/<connector-name>-migrations.md`.
This document should contain a section for each breaking change, in order of the version descending. It must explain users which action to take to migrate to the new version.
### Connectors must have user facing documentation
*Applies to the following connector types: source, destination*
*Applies to the following connector languages: java, low-code, python*
*Applies to connector with any support level*

The user facing connector documentation should be stored under `./docs/integrations/<connector-type>s/<connector-name>.md`.
### Connectors must have a changelog entry for each version
*Applies to the following connector types: source, destination*
*Applies to the following connector languages: java, low-code, python*
*Applies to connector with any support level*

Each new version of a connector must have a changelog entry defined in the user facing documentation in `./docs/integrations/<connector-type>s/<connector-name>.md`.

Expand All @@ -31,54 +34,70 @@ Each new version of a connector must have a changelog entry defined in the user
### Connectors must have valid metadata.yaml file
*Applies to the following connector types: source, destination*
*Applies to the following connector languages: java, low-code, python*
*Applies to connector with any support level*

Connectors must have a `metadata.yaml` file at the root of their directory. This file is used to build our connector registry. Its structure must follow our metadata schema. Field values are also validated. This is to ensure that all connectors have the required metadata fields and that the metadata is valid. More details in this [documentation](https://docs.airbyte.com/connector-development/connector-metadata-file).
### Connector must have a language tag in metadata
*Applies to the following connector types: source, destination*
*Applies to the following connector languages: java, low-code, python*
*Applies to connector with any support level*

Connectors must have a language tag in their metadata. It must be set in the `tags` field in metadata.yaml. The values can be `language:python` or `language:java`. This checks infers the correct language tag based on the presence of certain files in the connector directory.
### Python connectors must have a CDK tag in metadata
*Applies to the following connector types: source, destination*
*Applies to the following connector languages: python, low-code*
*Applies to connector with any support level*

Python connectors must have a CDK tag in their metadata. It must be set in the `tags` field in metadata.yaml. The values can be `cdk:low-code`, `cdk:python`, or `cdk:file`.
### Breaking change deadline should be a week in the future
*Applies to the following connector types: source, destination*
*Applies to the following connector languages: java, low-code, python*
*Applies to connector with any support level*

If the connector version has a breaking change, the deadline field must be set to at least a week in the future.
### Certified source connector must have a value filled out for maxSecondsBetweenMessages in metadata
*Applies to the following connector types: source*
*Applies to the following connector languages: java, low-code, python*
*Applies to connector with certified support level*

Certified source connectors must have a value filled out for `maxSecondsBetweenMessages` in metadata. This value represents the maximum number of seconds we could expect between messages for API connectors. And it's used by platform to tune connectors heartbeat timeout. The value must be set in the 'data' field in connector's `metadata.yaml` file.

## 📦 Packaging

### Connectors must use Poetry for dependency management
*Applies to the following connector types: source, destination*
*Applies to the following connector languages: python, low-code*
*Applies to connector with any support level*

Connectors must use [Poetry](https://python-poetry.org/) for dependency management. This is to ensure that all connectors use a dependency management tool which locks dependencies and ensures reproducible installs.
### Connectors must be licensed under MIT or Elv2
*Applies to the following connector types: source, destination*
*Applies to the following connector languages: java, low-code, python*
*Applies to connector with any support level*

Connectors must be licensed under the MIT or Elv2 license. This is to ensure that all connectors are licensed under a permissive license. More details in our [License FAQ](https://docs.airbyte.com/developer-guides/licenses/license-faq).
### Connector license in metadata.yaml and pyproject.toml file must match
*Applies to the following connector types: source, destination*
*Applies to the following connector languages: python, low-code*
*Applies to connector with any support level*

Connectors license in metadata.yaml and pyproject.toml file must match. This is to ensure that all connectors are consistently licensed.
### Connector version must follow Semantic Versioning
*Applies to the following connector types: source, destination*
*Applies to the following connector languages: java, low-code, python*
*Applies to connector with any support level*

Connector version must follow the Semantic Versioning scheme. This is to ensure that all connectors follow a consistent versioning scheme. Refer to our [Semantic Versioning for Connectors](https://docs.airbyte.com/contributing-to-airbyte/#semantic-versioning-for-connectors) for more details.
### Connector version in metadata.yaml and pyproject.toml file must match
*Applies to the following connector types: source, destination*
*Applies to the following connector languages: python, low-code*
*Applies to connector with any support level*

Connector version in metadata.yaml and pyproject.toml file must match. This is to ensure that connector release is consistent.
### Python connectors must have PyPi publishing enabled
*Applies to the following connector types: source*
*Applies to the following connector languages: python, low-code*
*Applies to connector with any support level*

Python connectors must have [PyPi](https://pypi.org/) publishing enabled in their `metadata.yaml` file. This is declared by setting `remoteRegistries.pypi.enabled` to `true` in metadata.yaml. This is to ensure that all connectors can be published to PyPi and can be used in `PyAirbyte`.

Expand All @@ -87,6 +106,7 @@ Python connectors must have [PyPi](https://pypi.org/) publishing enabled in thei
### Connectors must have an icon
*Applies to the following connector types: source, destination*
*Applies to the following connector languages: java, low-code, python*
*Applies to connector with any support level*

Each connector must have an icon available in at the root of the connector code directory. It must be an SVG file named `icon.svg` and must be a square.

Expand All @@ -95,11 +115,13 @@ Each connector must have an icon available in at the root of the connector code
### Connectors must use HTTPS only
*Applies to the following connector types: source, destination*
*Applies to the following connector languages: java, low-code, python*
*Applies to connector with any support level*

Connectors must use HTTPS only when making requests to external services.
### Python connectors must not use a Dockerfile and must declare their base image in metadata.yaml file
*Applies to the following connector types: source, destination*
*Applies to the following connector languages: python, low-code*
*Applies to connector with any support level*

Connectors must use our Python connector base image (`docker.io/airbyte/python-connector-base`), declared through the `connectorBuildOptions.baseImage` in their `metadata.yaml`.
This is to ensure that all connectors use a base image which is maintained and has security updates.

0 comments on commit 17a374e

Please sign in to comment.