Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DM-24785] Felis yaml validator #101

Merged
merged 4 commits into from
Dec 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/felislint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Felis Linter

"on": [push]

jobs:
build:
runs-on: ubuntu-latest

# Only do Docker builds of ticket branches and tagged releases.
if: startsWith(github.ref, 'refs/tags/') || startsWith(github.ref, 'refs/heads/tickets/')

steps:
- name: Check out repo
uses: actions/checkout@v2

- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: "3.10"
cache: "pip"

- name: Install dependencies
id: depend
run: pip install -r requirements.txt
working-directory: ./tap-schema

- name: Validate YAML files
id: validate-yaml
run: ./validator ../yml/*.yaml
working-directory: ./tap-schema
94 changes: 94 additions & 0 deletions tap-schema/validator
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/usr/bin/python3
import click
import sys
import yaml

@click.command()
@click.argument('felis_files', nargs=-1)
def validate_files(felis_files):
"""Starting place for all the validators. Loads the file and passes
the data into each one of the checks in turn. Each of those checks
runs through the file looking for what it is looking for and reports
errors."""
success = True

for f in felis_files:
click.echo(f"file {f}:")

with open(f) as orig_file:
felis_yaml = yaml.safe_load(orig_file.read())

if not check_table_indexes(felis_yaml):
success = False

if not check_descriptions(felis_yaml):
success = False

if not check_principal(felis_yaml):
success = False

if success:
sys.exit(0)
else:
sys.exit(1)


def check_descriptions(felis_yaml):
"""Look to make sure each column has a non empty description."""
success = True

for table in felis_yaml["tables"]:
for column in table["columns"]:
description = column.get("description", None)
if description == None:
table_name = table["name"]
column_name = column["name"]
click.echo(f"{table_name}:{column_name} has no description")
success = False

return success


def check_table_indexes(felis_yaml):
"""Look for table indexes on all the tables, and make sure that each index
is only used for one table."""
success = True
table_indicies = set()
for table in felis_yaml["tables"]:
table_name = table["name"]
table_index = table.get("tap:table_index", None)

if table_index == None or table_index == 0:
click.echo(f"{table_name} does not have a table index")
success = False
elif table_index in table_indicies:
click.echo(f"{table_name} duplicates table index of {table_index}")
success = False
else:
table_indicies.add(table_index)

return success


def check_principal(felis_yaml):
"""Look to make sure there is at least one column that is marked as
principal for each table."""
success = True

for table in felis_yaml["tables"]:
principal_columns = set()
for column in table["columns"]:
principal = column.get("tap:principal", 0)
if principal == 1:
principal_columns.add(column["name"])

if not principal_columns:
table_name = table["name"]
click.echo(f"{table_name} has no principal columns")
success = False

return success


if __name__ == '__main__':
validate_files()