diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53e4491..f703f7b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,8 @@ jobs: npm ci - name: Lint run: npm run lint + - name: Install js-yaml (optional dependency) + run: npm install --no-save js-yaml - name: Run tests run: npm test env: diff --git a/README.md b/README.md index 803df23..9a18aa2 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,10 @@ If your LookML project doesn't have a manifest.lkml file, you may want to consid - **rule: rule_name** - Recommended. Used to opt-in to built-in rules and to specify custom rules. See [customizing LAMS](https://looker-open-source.github.io/look-at-me-sideways/customizing-lams) - **rule_exemptions** - Optional. Originally used in 1 & v2 to opt-out of rules globally. A global opt-out can still be useful for opting-out of certain "sub rules" that a rule may return without opting-out of the entire rule. See [customizing LAMS](https://looker-open-source.github.io/look-at-me-sideways/customizing-lams) +### Optional Dependencies + +- `js-yaml` is not automatically installed with LAMS, but you may explicitly install it if you want to write custom rules that lint against the contents of a LookML Dashboard, which is a YAML-based file. In this case, also make sure to pass a `source` argument, as the default `source` does not includes `.dashboard.lookml` files. + ## About ### Release Notes diff --git a/__tests__/dummy-projects/28-lookml-dashboards/index.test.js b/__tests__/dummy-projects/28-lookml-dashboards/index.test.js new file mode 100644 index 0000000..9d328f2 --- /dev/null +++ b/__tests__/dummy-projects/28-lookml-dashboards/index.test.js @@ -0,0 +1,51 @@ +const lams = require('../../../index.js') +const mocks = require('../../../lib/mocks.js') +const path= require('path') +const options = { + reporting:"no", + cwd:__dirname, + source:"{manifest.lkml,*.model.lkml,*.dashboard.lookml}" +}; +require('../../../lib/expect-to-contain-message'); +const log = x=>console.log(x) +const testProjectName = __dirname.split(path.sep).slice(-1)[0]; + +describe('Projects', () => { + describe(testProjectName + " (needs js-yaml optional dependency)", () => { + let {spies, process, console} = mocks() + let messages + beforeAll( async () => { + messages = await lams(options,{process, console}) + }) + it("should not error out", ()=> { + expect(console.error).not.toHaveBeenCalled() + }); + it("it should not contain any unexpected parser (P0) errors", ()=> { + expect({messages}).not.toContainMessage({ + rule: "P0", + level: "error" + }); + }); + it("it should not contain any parser syntax (P1) errors", ()=> { + expect({messages}).not.toContainMessage({ + rule: "P1", + level: "error" + }); + }); + + it("it should match DASH_1 (3 match, 0 exempt, 1 error)", ()=> { + expect({messages}).toContainMessage({ + rule: "DASH_1", + level: "info", + description: "Rule DASH_1 summary: 3 matches, 0 matches exempt, and 1 errors" + }); + }); + + it("it should error on DASH_1", ()=> { + expect({messages}).toContainMessage({ + rule: "DASH_1", + level: "error" + }); + }); + }); +}); diff --git a/__tests__/dummy-projects/28-lookml-dashboards/manifest.lkml b/__tests__/dummy-projects/28-lookml-dashboards/manifest.lkml new file mode 100644 index 0000000..8b791fc --- /dev/null +++ b/__tests__/dummy-projects/28-lookml-dashboards/manifest.lkml @@ -0,0 +1,8 @@ +#LAMS +#rule: DASH_1 { +# description: "`model` attribute should not be set (allow inheritance based on model inclusion)" +# match: "$.model.*.dashboard.*[0].elements.*" +# expr_rule: +# (=== ::match:model undefined) +# ;; +#} diff --git a/__tests__/dummy-projects/28-lookml-dashboards/test.dashboard.lookml b/__tests__/dummy-projects/28-lookml-dashboards/test.dashboard.lookml new file mode 100644 index 0000000..af128ae --- /dev/null +++ b/__tests__/dummy-projects/28-lookml-dashboards/test.dashboard.lookml @@ -0,0 +1,97 @@ +- dashboard: user_segments + title: User segments + layout: newspaper + embed_style: + background_color: "#ff4141" + show_title: false + title_color: "#3a4245" + show_filters_bar: true + tile_text_color: "#3a4245" + text_tile_text_color: "#caff37" + elements: + - name: User segments + title: User segments + model: thelook + explore: users + type: looker_pie + fields: + - users.gender + - users.count + filters: + users.state: '' + sorts: + - users.count desc + limit: 500 + column_limit: 50 + query_timezone: America/Los_Angeles + value_labels: legend + label_type: labPer + stacking: '' + show_value_labels: false + label_density: 25 + legend_position: center + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: true + limit_displayed_rows: false + y_axis_combined: true + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + x_axis_scale: auto + y_axis_scale_mode: linear + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + series_types: {} + listen: + State: users.state + row: 0 + col: 0 + width: 12 + height: 8 + - name: Markdown + type: text + title_text: Markdown + body_text: "Inline-style: \n![alt text](https://wwwstatic-a.lookercdn.com/homepage/new_home/looker.svg)\n\ + \nReference-style: \n![alt text][logo]\n\n[logo]: https://wwwstatic-a.lookercdn.com/homepage/new_home/looker.svg" + row: 0 + col: 12 + width: 12 + height: 6 + - name: Md + type: text + title_text: Md + body_text: "Inline-style: \n![alt text](https://wwwstatic-a.lookercdn.com/homepage/new_home/looker.svg)\n\ + \nReference-style: \n![alt text][logo]\n\n[logo]: https://wwwstatic-a.lookercdn.com/homepage/new_home/looker.svg" + row: 8 + col: 0 + width: 24 + height: 8 + filters: + - name: State + title: State + type: field_filter + default_value: + model: thelook + explore: orderss + field: users.state + listens_to_filters: [] + allow_multiple_values: true + required: false + - name: City + title: City + type: field_filter + default_value: '' + model: thelook + explore: orders + field: users.city + listens_to_filters: + - State + allow_multiple_values: true + required: false diff --git a/__tests__/dummy-projects/28-lookml-dashboards/test.model.lkml b/__tests__/dummy-projects/28-lookml-dashboards/test.model.lkml new file mode 100644 index 0000000..7796508 --- /dev/null +++ b/__tests__/dummy-projects/28-lookml-dashboards/test.model.lkml @@ -0,0 +1 @@ +include: "*.dashboard" diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 42982ea..af1abab 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -13,7 +13,7 @@ "fromentries": "^1.3.2", "jsonpath-plus": "^7.2.0", "liyad": "^0.2.4", - "lookml-parser": "^6.8.2", + "lookml-parser": "^6.9.1", "minimist": "^1.2.6", "require-from-string": "^2.0.2" }, @@ -1393,7 +1393,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "devOptional": true }, "node_modules/asynckit": { "version": "0.4.0", @@ -1538,12 +1538,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -2320,9 +2320,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -3613,7 +3613,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, + "devOptional": true, "dependencies": { "argparse": "^2.0.1" }, @@ -3806,9 +3806,9 @@ "dev": true }, "node_modules/lookml-parser": { - "version": "6.8.2", - "resolved": "https://registry.npmjs.org/lookml-parser/-/lookml-parser-6.8.2.tgz", - "integrity": "sha512-iuhCtKrbr4iuQ1DkiHi4y3yepK5PqttEdZMYP8w9gpm0tr0cB4rAqqpHIcndoiy+m6wnS3UfhjK3pFP8AIrXow==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/lookml-parser/-/lookml-parser-6.9.1.tgz", + "integrity": "sha512-2xq76z1Igpnz1/j0o14qxHp6FpWfDRvmDVKjk71RouLW81yTp6NAqwyCM5r0UdjIgc9RTPGNY6G2DwG1XBWqFg==", "dependencies": { "bluebird": "^3.5.1", "glob": "^7.1.2", @@ -3817,6 +3817,14 @@ }, "bin": { "lookml-parser": "cli.js" + }, + "peerDependencies": { + "js-yaml": "^4.1.0" + }, + "peerDependenciesMeta": { + "js-yaml": { + "optional": true + } } }, "node_modules/lru-cache": { @@ -4950,9 +4958,9 @@ } }, "node_modules/ws": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "engines": { "node": ">=8.3.0" @@ -6105,7 +6113,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "devOptional": true }, "asynckit": { "version": "0.4.0", @@ -6225,12 +6233,12 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "browser-process-hrtime": { @@ -6821,9 +6829,9 @@ } }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -7820,7 +7828,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, + "devOptional": true, "requires": { "argparse": "^2.0.1" } @@ -7959,9 +7967,9 @@ "dev": true }, "lookml-parser": { - "version": "6.8.2", - "resolved": "https://registry.npmjs.org/lookml-parser/-/lookml-parser-6.8.2.tgz", - "integrity": "sha512-iuhCtKrbr4iuQ1DkiHi4y3yepK5PqttEdZMYP8w9gpm0tr0cB4rAqqpHIcndoiy+m6wnS3UfhjK3pFP8AIrXow==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/lookml-parser/-/lookml-parser-6.9.1.tgz", + "integrity": "sha512-2xq76z1Igpnz1/j0o14qxHp6FpWfDRvmDVKjk71RouLW81yTp6NAqwyCM5r0UdjIgc9RTPGNY6G2DwG1XBWqFg==", "requires": { "bluebird": "^3.5.1", "glob": "^7.1.2", @@ -8841,9 +8849,9 @@ } }, "ws": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "requires": {} }, diff --git a/package.json b/package.json index 4353fc2..4c1c5e1 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "fromentries": "^1.3.2", "jsonpath-plus": "^7.2.0", "liyad": "^0.2.4", - "lookml-parser": "^6.8.2", + "lookml-parser": "^6.9.1", "minimist": "^1.2.6", "require-from-string": "^2.0.2" },