Skip to content

Commit 719446f

Browse files
authored
Merge pull request #13 from rbob86/additional_lookml_rules
Added two more rules + tests
2 parents 3c0204b + 9c1b3dd commit 719446f

6 files changed

+110
-0
lines changed

config.example.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
severity: warning
33
- rule: view_with_many_fields_requires_fields_hidden_by_default
44
severity: ignore
5+
- rule: view_with_dimensions_and_measures_has_one_primary_key_defined
6+
severity: ignore
57
- rule: dimension_group_of_type_time_requires_datatype
68
severity: error
79
- rule: dimension_group_of_type_time_requires_timeframe
810
severity: warning
11+
- rule: explore_joins_require_relationship
12+
severity: ignore
913
- rule: explore_requires_description
1014
severity: ignore
1115
- rule: field_sql_html_requires_user_attribute_when_search_terms_found

linter/rule_factory.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
# TODO: AUTO-IMPORT RULES
66
from linter.rules.field_requires_description import FieldRequiresDescription
77
from linter.rules.view_with_many_fields_requires_fields_hidden_by_default import ViewWithManyFieldsRequiresFieldsHiddenByDefault
8+
from linter.rules.view_with_dimensions_and_measures_has_one_primary_key import ViewWithDimensionsAndMeasuresHasOnePrimaryKey
89
from linter.rules.dimension_group_of_type_time_requires_datatype import DimensionGroupOfTypeTimeRequiresDatatype
910
from linter.rules.dimension_group_of_type_time_requires_timeframe import DimensionGroupOfTypeTimeRequiresTimeframe
1011
from linter.rules.explore_requires_description import ExploreRequiresDescription
12+
from linter.rules.explore_joins_require_relationship import ExploreJoinsRequireRelationship
1113
from linter.rules.field_sql_html_requires_user_attribute_when_search_terms_found import FieldSqlHtmlRequiresUserAttributeWhenSearchTermsFound
1214

1315

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from linter.rule import Rule
2+
3+
4+
class ExploreJoinsRequireRelationship(Rule):
5+
def applies_to():
6+
return ('explore',)
7+
8+
def run(self, explore):
9+
joins = explore.get('joins', [])
10+
for join in joins:
11+
if not 'relationship' in join:
12+
return False
13+
return True
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from linter.rule import Rule
2+
3+
4+
class ViewWithDimensionsAndMeasuresHasOnePrimaryKey(Rule):
5+
def applies_to():
6+
return ('view',)
7+
8+
def run(self, view):
9+
dimensions, measures = [
10+
view.get(key, []) for key in ['dimensions', 'measures']]
11+
has_primary_key = True
12+
if len(measures) > 0 and len(dimensions) > 0:
13+
has_primary_key = False
14+
for dimension in dimensions:
15+
if 'primary_key' in dimension:
16+
if has_primary_key:
17+
return False
18+
has_primary_key = True
19+
return has_primary_key
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from linter.rule import Severity
2+
from linter.rules.explore_joins_require_relationship import ExploreJoinsRequireRelationship
3+
4+
5+
def test_run_method_successfully_validates_explore_with_joins_and_relationships() -> None:
6+
rule = ExploreJoinsRequireRelationship(Severity.ERROR.value)
7+
8+
explore = {'sql_always_where': "${products.category} in\n (select ${products.category} from ${products.SQL_TABLE_NAME} products\n where ${products.brand} = 'Allegra K'\n group by 1)", 'joins': [{'type': 'left_outer', 'sql_on': '${order_items.user_id} = ${users.id}', 'relationship': 'many_to_one', 'name': 'users'}, {'fields': [
9+
'inventory_items.id'], 'type': 'left_outer', 'sql_on': '${order_items.inventory_item_id} = ${inventory_items.id}', 'relationship': 'many_to_one', 'name': 'inventory_items'}, {'type': 'left_outer', 'sql_on': '${inventory_items.product_id} = ${products.id}', 'relationship': 'many_to_one', 'name': 'products'}], 'name': 'order_items'}
10+
rule_result = rule.run(explore)
11+
assert rule_result == True
12+
13+
14+
def test_run_method_successfully_validates_explore_without_joins() -> None:
15+
rule = ExploreJoinsRequireRelationship(Severity.ERROR.value)
16+
17+
explore = {
18+
'sql_always_where': "${products.category} in\n (select ${products.category} from ${products.SQL_TABLE_NAME} products\n where ${products.brand} = 'Allegra K'\n group by 1)", 'name': 'order_items'}
19+
rule_result = rule.run(explore)
20+
assert rule_result == True
21+
22+
23+
def test_run_method_fails_to_validate_explore_with_joins_and_without_relationship() -> None:
24+
rule = ExploreJoinsRequireRelationship(Severity.ERROR.value)
25+
26+
explore = {'sql_always_where': "${products.category} in\n (select ${products.category} from ${products.SQL_TABLE_NAME} products\n where ${products.brand} = 'Allegra K'\n group by 1)", 'joins': [{'type': 'left_outer', 'sql_on': '${order_items.user_id} = ${users.id}', 'name': 'users'}, {'fields': [
27+
'inventory_items.id'], 'type': 'left_outer', 'sql_on': '${order_items.inventory_item_id} = ${inventory_items.id}', 'relationship': 'many_to_one', 'name': 'inventory_items'}, {'type': 'left_outer', 'sql_on': '${inventory_items.product_id} = ${products.id}', 'relationship': 'many_to_one', 'name': 'products'}], 'name': 'order_items'}
28+
rule_result = rule.run(explore)
29+
assert rule_result == False
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from linter.rule import Severity
2+
from linter.rules.view_with_dimensions_and_measures_has_one_primary_key import ViewWithDimensionsAndMeasuresHasOnePrimaryKey
3+
4+
5+
def test_run_method_successfully_validates_view_file_with_dimensions_and_measures_has_one_primary_key() -> None:
6+
rule = ViewWithDimensionsAndMeasuresHasOnePrimaryKey(
7+
Severity.ERROR.value)
8+
9+
view = {'sql_table_name': 'public.products', 'dimensions': [{'primary_key': 'yes', 'type': 'number', 'sql': 'CASE WHEN {{ _user_attributes[""permissions_financial_row_level""] }} = 1 THEN\n ${TABLE}.id\n ELSE\n -1\n END',
10+
'html': '{% if _user_attributes[""permissions_financial_row_level""] == 1 %}\n {{ rendered_value }}\n {% else %}\n [Insufficient Permissions]\n {% endif %}', 'name': 'id'}], 'name': 'products'}
11+
rule_result = rule.run(view)
12+
assert rule_result == True
13+
14+
15+
def test_run_method_successfully_validates_view_file_with_dimensions_and_no_measures_does_not_require_primary_key() -> None:
16+
rule = ViewWithDimensionsAndMeasuresHasOnePrimaryKey(
17+
Severity.ERROR.value)
18+
19+
view = {'sql_table_name': 'public.products',
20+
'dimensions': range(0, 50), 'name': 'total_cost'}
21+
rule_result = rule.run(view)
22+
assert rule_result == True
23+
24+
25+
def test_run_method_successfully_validates_view_file_with_no_dimensions_and_measures_does_not_require_primary_key() -> None:
26+
rule = ViewWithDimensionsAndMeasuresHasOnePrimaryKey(
27+
Severity.ERROR.value)
28+
29+
view = {'sql_table_name': 'public.products',
30+
'measure': range(0, 50), 'name': 'total_cost'}
31+
rule_result = rule.run(view)
32+
assert rule_result == True
33+
34+
35+
def test_run_method_fails_when_view_with_dimensions_and_measures_does_not_have_a_primary_key() -> None:
36+
rule = ViewWithDimensionsAndMeasuresHasOnePrimaryKey(
37+
Severity.ERROR.value)
38+
39+
view = {'sql_table_name': 'public.products',
40+
'dimensions': [str(dim) for dim in range(0, 50)], 'name': 'this_view',
41+
'measures': [str(measure) for measure in range(0, 50)]}
42+
rule_result = rule.run(view)
43+
assert rule_result == False

0 commit comments

Comments
 (0)