@lightdash/cli 2.152.0 → 2.153.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { DbtDoc, DbtModelNode, DbtSchemaEditor, SupportedDbtVersions } from '@lightdash/common';
1
+ import { DbtDoc, DbtModelNode, DbtSchemaEditor, SupportedDbtVersions, type WarehouseCatalog } from '@lightdash/common';
2
2
  import { WarehouseClient, WarehouseTableSchema } from '@lightdash/warehouses';
3
3
  type CompiledModel = {
4
4
  name: string;
@@ -14,6 +14,17 @@ type GetDatabaseTableForModelArgs = {
14
14
  warehouseClient: WarehouseClient;
15
15
  preserveColumnCase: boolean;
16
16
  };
17
+ type TableRef = {
18
+ database: string;
19
+ schema: string;
20
+ table: string;
21
+ };
22
+ /**
23
+ * Only scalar leaves reachable through non-repeated structs can be queried as
24
+ * plain columns today, so generated YAML skips container nodes and anything
25
+ * inside a repeated node.
26
+ */
27
+ export declare const isGeneratableColumn: (catalog: WarehouseCatalog, { database, schema, table }: TableRef, columnPath: string) => boolean;
17
28
  export declare const getWarehouseTableForModel: ({ model, warehouseClient, preserveColumnCase, }: GetDatabaseTableForModelArgs) => Promise<WarehouseTableSchema>;
18
29
  export declare const isDocBlock: (text?: string | undefined) => boolean;
19
30
  type FindAndUpdateModelYamlArgs = {
@@ -1 +1 @@
1
- {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/dbt/models.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,MAAM,EACN,YAAY,EACZ,eAAe,EAIf,oBAAoB,EACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAQ9E,KAAK,aAAa,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,KAAK,4BAA4B,GAAG;IAChC,KAAK,EAAE,aAAa,CAAC;IACrB,eAAe,EAAE,eAAe,CAAC;IACjC,kBAAkB,EAAE,OAAO,CAAC;CAC/B,CAAC;AACF,eAAO,MAAM,yBAAyB,oDAInC,4BAA4B,KAAG,OAAO,CAAC,oBAAoB,CA6B7D,CAAC;AAyDF,eAAO,MAAM,UAAU,UAAU,MAAM,GAAG,SAAS,KAAQ,OACX,CAAC;AA8BjD,KAAK,0BAA0B,GAAG;IAC9B,KAAK,EAAE,aAAa,CAAC;IACrB,KAAK,EAAE,oBAAoB,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,oBAAoB,CAAC;CACrC,CAAC;AACF,eAAO,MAAM,sBAAsB,yFAShC,0BAA0B,KAAG,OAAO,CAAC;IACpC,UAAU,EAAE,eAAe,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;CAC1B,CAoKA,CAAC;AAEF,eAAO,MAAM,iBAAiB,WAClB,YAAY,EAAE,QAChB;IACF,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7B,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC9B,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B,KACF,OAAO,CAAC,aAAa,EAAE,CAoEzB,CAAC"}
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/dbt/models.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,MAAM,EACN,YAAY,EACZ,eAAe,EAKf,oBAAoB,EACpB,KAAK,gBAAgB,EACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAQ9E,KAAK,aAAa,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,KAAK,4BAA4B,GAAG;IAChC,KAAK,EAAE,aAAa,CAAC;IACrB,eAAe,EAAE,eAAe,CAAC;IACjC,kBAAkB,EAAE,OAAO,CAAC;CAC/B,CAAC;AACF,KAAK,QAAQ,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,YACnB,gBAAgB,+BACI,QAAQ,cACzB,MAAM,KACnB,OAqBF,CAAC;AAEF,eAAO,MAAM,yBAAyB,oDAInC,4BAA4B,KAAG,OAAO,CAAC,oBAAoB,CAgC7D,CAAC;AAyDF,eAAO,MAAM,UAAU,UAAU,MAAM,GAAG,SAAS,KAAQ,OACX,CAAC;AA8BjD,KAAK,0BAA0B,GAAG;IAC9B,KAAK,EAAE,aAAa,CAAC;IACrB,KAAK,EAAE,oBAAoB,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,oBAAoB,CAAC;CACrC,CAAC;AACF,eAAO,MAAM,sBAAsB,yFAShC,0BAA0B,KAAG,OAAO,CAAC;IACpC,UAAU,EAAE,eAAe,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;CAC1B,CAoKA,CAAC;AAEF,eAAO,MAAM,iBAAiB,WAClB,YAAY,EAAE,QAChB;IACF,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7B,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC9B,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B,KACF,OAAO,CAAC,aAAa,EAAE,CAoEzB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getCompiledModels = exports.findAndUpdateModelYaml = exports.isDocBlock = exports.getWarehouseTableForModel = void 0;
3
+ exports.getCompiledModels = exports.findAndUpdateModelYaml = exports.isDocBlock = exports.getWarehouseTableForModel = exports.isGeneratableColumn = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const common_1 = require("@lightdash/common");
6
6
  const execa_1 = tslib_1.__importDefault(require("execa"));
@@ -9,6 +9,27 @@ const path = tslib_1.__importStar(require("path"));
9
9
  const globalState_1 = tslib_1.__importDefault(require("../globalState"));
10
10
  const styles = tslib_1.__importStar(require("../styles"));
11
11
  const schema_1 = require("./schema");
12
+ /**
13
+ * Only scalar leaves reachable through non-repeated structs can be queried as
14
+ * plain columns today, so generated YAML skips container nodes and anything
15
+ * inside a repeated node.
16
+ */
17
+ const isGeneratableColumn = (catalog, { database, schema, table }, columnPath) => {
18
+ const segments = columnPath.split('.');
19
+ return segments.every((_, index) => {
20
+ const prefix = segments.slice(0, index + 1).join('.');
21
+ const shape = (0, common_1.getCatalogNestedColumnShape)(catalog, database, schema, table, prefix);
22
+ const isLeaf = index === segments.length - 1;
23
+ // An array of scalars is exposed through its container entry, so it
24
+ // is generated like a leaf; every other container and anything under
25
+ // an array is left for the user to document by hand.
26
+ const isScalarArray = shape?.repeated === true && shape.record === false;
27
+ return isLeaf
28
+ ? shape === undefined || isScalarArray
29
+ : shape?.repeated === false;
30
+ });
31
+ };
32
+ exports.isGeneratableColumn = isGeneratableColumn;
12
33
  const getWarehouseTableForModel = async ({ model, warehouseClient, preserveColumnCase, }) => {
13
34
  const tableRef = {
14
35
  database: model.database,
@@ -26,6 +47,9 @@ const getWarehouseTableForModel = async ({ model, warehouseClient, preserveColum
26
47
  throw new common_1.ParseError(`Expected to find materialised model at ${tableRef.database}.${tableRef.schema}.${tableRef.table} but couldn't find (or cannot access) ${missing}`);
27
48
  }
28
49
  return Object.entries(table).reduce((accumulator, [key, value]) => {
50
+ if (!(0, exports.isGeneratableColumn)(catalog, tableRef, key)) {
51
+ return accumulator;
52
+ }
29
53
  const columnName = preserveColumnCase ? key : key.toLowerCase();
30
54
  accumulator[columnName] = value;
31
55
  return accumulator;
@@ -1,7 +1,48 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const common_1 = require("@lightdash/common");
3
4
  const models_1 = require("./models");
4
5
  describe('Models', () => {
6
+ describe('isGeneratableColumn', () => {
7
+ const ref = { database: 'db', schema: 'ds', table: 'sessions' };
8
+ const catalog = {
9
+ db: {
10
+ ds: {
11
+ sessions: {
12
+ visitId: common_1.DimensionType.NUMBER,
13
+ totals: common_1.DimensionType.STRING,
14
+ 'totals.pageviews': common_1.DimensionType.NUMBER,
15
+ hits: common_1.DimensionType.STRING,
16
+ 'hits.page': common_1.DimensionType.STRING,
17
+ 'hits.page.pagePath': common_1.DimensionType.STRING,
18
+ tags: common_1.DimensionType.STRING,
19
+ },
20
+ },
21
+ },
22
+ };
23
+ const shapes = {
24
+ totals: { repeated: false, record: true },
25
+ hits: { repeated: true, record: true },
26
+ 'hits.page': { repeated: false, record: true },
27
+ tags: { repeated: true, record: false },
28
+ };
29
+ Object.entries(shapes).forEach(([path, shape]) => (0, common_1.setCatalogNestedColumnShape)(catalog, ref.database, ref.schema, ref.table, path, shape));
30
+ test('keeps scalar columns and leaves under plain structs', () => {
31
+ expect((0, models_1.isGeneratableColumn)(catalog, ref, 'visitId')).toBe(true);
32
+ expect((0, models_1.isGeneratableColumn)(catalog, ref, 'totals.pageviews')).toBe(true);
33
+ });
34
+ test('keeps an array of scalars, exposed through its container entry', () => {
35
+ expect((0, models_1.isGeneratableColumn)(catalog, ref, 'tags')).toBe(true);
36
+ });
37
+ test('skips struct and repeated record containers', () => {
38
+ expect((0, models_1.isGeneratableColumn)(catalog, ref, 'totals')).toBe(false);
39
+ expect((0, models_1.isGeneratableColumn)(catalog, ref, 'hits')).toBe(false);
40
+ });
41
+ test('skips leaves beneath a repeated node', () => {
42
+ expect((0, models_1.isGeneratableColumn)(catalog, ref, 'hits.page')).toBe(false);
43
+ expect((0, models_1.isGeneratableColumn)(catalog, ref, 'hits.page.pagePath')).toBe(false);
44
+ });
45
+ });
5
46
  describe('isDocBlock', () => {
6
47
  test('should match all doc block variations', () => {
7
48
  expect((0, models_1.isDocBlock)("{{doc('user_id')}}")).toBe(true); // single quote
@@ -1 +1 @@
1
- {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/handlers/compile.ts"],"names":[],"mappings":"AAAA,OAAO,EAOH,OAAO,EACP,YAAY,EAgBf,MAAM,mBAAmB,CAAC;AAqB3B,OAAO,EAAE,iBAAiB,EAA8B,MAAM,eAAe,CAAC;AAI9E,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,GAAG;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IAC/B,QAAQ,EAAE,CAAC,OAAO,GAAG,YAAY,CAAC,EAAE,CAAC;IACrC,iBAAiB,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,eAAO,MAAM,uBAAuB,YACvB,OAAO,GAAG,YAAY,KAChC,OAIW,CAAC;AAEf,eAAO,MAAM,0BAA0B,YAC1B,OAAO,GAAG,YAAY,KAChC,OAAO,GAAG,YAiBZ,CAAC;AA6TF,eAAO,MAAM,cAAc,YACd,qBAAqB,KAC/B,OAAO,CAAC,oBAAoB,CA0a9B,CAAC;AAEF,eAAO,MAAM,OAAO,YACP,qBAAqB,KAC/B,OAAO,CAAC,CAAC,OAAO,GAAG,YAAY,CAAC,EAAE,CACO,CAAC;AAE7C,eAAO,MAAM,cAAc,oBACN,qBAAqB,kBAmCzC,CAAC"}
1
+ {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/handlers/compile.ts"],"names":[],"mappings":"AAAA,OAAO,EAOH,OAAO,EACP,YAAY,EAkBf,MAAM,mBAAmB,CAAC;AAqB3B,OAAO,EAAE,iBAAiB,EAA8B,MAAM,eAAe,CAAC;AAsB9E,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,GAAG;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IAC/B,QAAQ,EAAE,CAAC,OAAO,GAAG,YAAY,CAAC,EAAE,CAAC;IACrC,iBAAiB,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,eAAO,MAAM,uBAAuB,YACvB,OAAO,GAAG,YAAY,KAChC,OAIW,CAAC;AAEf,eAAO,MAAM,0BAA0B,YAC1B,OAAO,GAAG,YAAY,KAChC,OAAO,GAAG,YAiBZ,CAAC;AA6TF,eAAO,MAAM,cAAc,YACd,qBAAqB,KAC/B,OAAO,CAAC,oBAAoB,CA2a9B,CAAC;AAEF,eAAO,MAAM,OAAO,YACP,qBAAqB,KAC/B,OAAO,CAAC,CAAC,OAAO,GAAG,YAAY,CAAC,EAAE,CACO,CAAC;AAE7C,eAAO,MAAM,cAAc,oBACN,qBAAqB,kBAmCzC,CAAC"}
@@ -19,6 +19,22 @@ const apiClient_1 = require("./dbt/apiClient");
19
19
  const compile_1 = require("./dbt/compile");
20
20
  const getDbtVersion_1 = require("./dbt/getDbtVersion");
21
21
  const getWarehouseClient_1 = tslib_1.__importDefault(require("./dbt/getWarehouseClient"));
22
+ // Compile-time behaviour the server gates behind a feature flag. Unreachable
23
+ // servers and older ones that don't know the flag both mean "off".
24
+ const getUnnestRepeatedColumns = async () => {
25
+ try {
26
+ const flag = await (0, apiClient_1.lightdashApi)({
27
+ method: 'GET',
28
+ url: `/api/v2/feature-flag/${common_1.FeatureFlags.UnnestRepeatedColumns}`,
29
+ body: undefined,
30
+ });
31
+ return flag.enabled;
32
+ }
33
+ catch (e) {
34
+ globalState_1.default.debug(`> Could not read the ${common_1.FeatureFlags.UnnestRepeatedColumns} flag, compiling without it: ${(0, common_1.getErrorMessage)(e)}`);
35
+ return false;
36
+ }
37
+ };
22
38
  const hasBlockingCompileError = (explore) => (0, common_1.isExploreError)(explore) ||
23
39
  explore.warnings?.some((warning) => warning.type === common_1.InlineErrorType.WAREHOUSE_COLUMN_ERROR) === true;
24
40
  exports.hasBlockingCompileError = hasBlockingCompileError;
@@ -414,6 +430,7 @@ const compileProject = async (options) => {
414
430
  disableTimestampConversion: options.disableTimestampConversion,
415
431
  allowPartialCompilation,
416
432
  postProcessors: [common_1.preAggregatePostProcessor],
433
+ unnestRepeatedColumns: await getUnnestRepeatedColumns(),
417
434
  });
418
435
  const validatedExplores = options.validateWarehouseColumns === true &&
419
436
  validationWarehouseClient
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightdash/cli",
3
- "version": "2.152.0",
3
+ "version": "2.153.0",
4
4
  "description": "Lightdash CLI tool",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -45,8 +45,8 @@
45
45
  "unique-names-generator": "4.7.1",
46
46
  "uuid": "11.1.1",
47
47
  "yaml": "2.8.3",
48
- "@lightdash/common": "2.152.0",
49
- "@lightdash/warehouses": "2.152.0"
48
+ "@lightdash/warehouses": "2.153.0",
49
+ "@lightdash/common": "2.153.0"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/inquirer": "8.2.4",