@lightdash/cli 2.209.2 → 2.211.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.
@@ -25,11 +25,24 @@ type TableRef = {
25
25
  * inside a repeated node.
26
26
  */
27
27
  export declare const isGeneratableColumn: (catalog: WarehouseCatalog, { database, schema, table }: TableRef, columnPath: string) => boolean;
28
- export declare const getWarehouseTableForModel: ({ model, warehouseClient, preserveColumnCase, }: GetDatabaseTableForModelArgs) => Promise<WarehouseTableSchema>;
28
+ export type WarehouseModelColumns = {
29
+ /** Columns `generate` writes to YAML: scalars, struct leaves and arrays of scalars. */
30
+ table: WarehouseTableSchema;
31
+ /** Every column path the warehouse reports, containers and nested leaves included. */
32
+ warehouseColumnPaths: string[];
33
+ };
34
+ export declare const getWarehouseTableForModel: ({ model, warehouseClient, preserveColumnCase, }: GetDatabaseTableForModelArgs) => Promise<WarehouseModelColumns>;
35
+ /**
36
+ * Existing YAML columns the warehouse no longer has. Nested leaves and
37
+ * containers are not generated, but they are real warehouse paths, so they
38
+ * are never reported as missing.
39
+ */
40
+ export declare const getColumnsMissingFromWarehouse: (existingColumnNames: string[], warehouseColumnPaths: string[]) => string[];
29
41
  export declare const isDocBlock: (text?: string | undefined) => boolean;
30
42
  type FindAndUpdateModelYamlArgs = {
31
43
  model: CompiledModel;
32
44
  table: WarehouseTableSchema;
45
+ warehouseColumnPaths: string[];
33
46
  docs: Record<string, DbtDoc>;
34
47
  includeMeta: boolean;
35
48
  projectDir: string;
@@ -37,7 +50,7 @@ type FindAndUpdateModelYamlArgs = {
37
50
  assumeYes: boolean;
38
51
  dbtVersion?: SupportedDbtVersions;
39
52
  };
40
- export declare const findAndUpdateModelYaml: ({ model, table, docs, includeMeta, projectDir, projectName, assumeYes, dbtVersion, }: FindAndUpdateModelYamlArgs) => Promise<{
53
+ export declare const findAndUpdateModelYaml: ({ model, table, warehouseColumnPaths, docs, includeMeta, projectDir, projectName, assumeYes, dbtVersion, }: FindAndUpdateModelYamlArgs) => Promise<{
41
54
  updatedYml: DbtSchemaEditor;
42
55
  outputFilePath: string;
43
56
  }>;
@@ -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,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
+ {"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,MAAM,MAAM,qBAAqB,GAAG;IAChC,uFAAuF;IACvF,KAAK,EAAE,oBAAoB,CAAC;IAC5B,sFAAsF;IACtF,oBAAoB,EAAE,MAAM,EAAE,CAAC;CAClC,CAAC;AAEF,eAAO,MAAM,yBAAyB,oDAInC,4BAA4B,KAAG,OAAO,CAAC,qBAAqB,CAoC9D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,wBAClB,MAAM,EAAE,wBACP,MAAM,EAAE,KAC/B,MAAM,EAKR,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,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,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,+GAUhC,0BAA0B,KAAG,OAAO,CAAC;IACpC,UAAU,EAAE,eAAe,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;CAC1B,CA8JA,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 = exports.isGeneratableColumn = void 0;
3
+ exports.getCompiledModels = exports.findAndUpdateModelYaml = exports.isDocBlock = exports.getColumnsMissingFromWarehouse = 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"));
@@ -46,16 +46,29 @@ const getWarehouseTableForModel = async ({ model, warehouseClient, preserveColum
46
46
  (table === undefined && `table ${tableRef.table}`);
47
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}`);
48
48
  }
49
- return Object.entries(table).reduce((accumulator, [key, value]) => {
50
- if (!(0, exports.isGeneratableColumn)(catalog, tableRef, key)) {
49
+ const columnName = (key) => preserveColumnCase ? key : key.toLowerCase();
50
+ return {
51
+ table: Object.entries(table).reduce((accumulator, [key, value]) => {
52
+ if (!(0, exports.isGeneratableColumn)(catalog, tableRef, key)) {
53
+ return accumulator;
54
+ }
55
+ accumulator[columnName(key)] = value;
51
56
  return accumulator;
52
- }
53
- const columnName = preserveColumnCase ? key : key.toLowerCase();
54
- accumulator[columnName] = value;
55
- return accumulator;
56
- }, {});
57
+ }, {}),
58
+ warehouseColumnPaths: Object.keys(table).map(columnName),
59
+ };
57
60
  };
58
61
  exports.getWarehouseTableForModel = getWarehouseTableForModel;
62
+ /**
63
+ * Existing YAML columns the warehouse no longer has. Nested leaves and
64
+ * containers are not generated, but they are real warehouse paths, so they
65
+ * are never reported as missing.
66
+ */
67
+ const getColumnsMissingFromWarehouse = (existingColumnNames, warehouseColumnPaths) => {
68
+ const known = new Set(warehouseColumnPaths.map((columnPath) => columnPath.toLowerCase()));
69
+ return existingColumnNames.filter((name) => !known.has(name.toLowerCase()));
70
+ };
71
+ exports.getColumnsMissingFromWarehouse = getColumnsMissingFromWarehouse;
59
72
  // Check if we should use dbt 1.10+ metadata structure
60
73
  const wrapInConfig = (dbtVersion, meta) => {
61
74
  const useDbt110Metadata = dbtVersion &&
@@ -112,7 +125,7 @@ const askOverwriteDescription = async (columnName, existingDescription, newDescr
112
125
  return newDescription;
113
126
  return existingDescription;
114
127
  };
115
- const findAndUpdateModelYaml = async ({ model, table, docs, includeMeta, projectDir, projectName, assumeYes, dbtVersion, }) => {
128
+ const findAndUpdateModelYaml = async ({ model, table, warehouseColumnPaths, docs, includeMeta, projectDir, projectName, assumeYes, dbtVersion, }) => {
116
129
  const generatedModel = generateModelYml({
117
130
  model,
118
131
  table,
@@ -185,11 +198,7 @@ const findAndUpdateModelYaml = async ({ model, table, docs, includeMeta, project
185
198
  newColumns.forEach((column) => {
186
199
  schemaEditor.addColumn(model.name, column);
187
200
  });
188
- // Delete columns that no longer exist in the warehouse (case-insensitive comparison)
189
- const generatedColumnNamesLower = generatedModel.columns.map((gc) => gc.name.toLowerCase());
190
- const deletedColumnNames = existingColumns
191
- .filter((c) => !generatedColumnNamesLower.includes(c.name.toLowerCase()))
192
- .map((c) => c.name);
201
+ const deletedColumnNames = (0, exports.getColumnsMissingFromWarehouse)(existingColumns.map((c) => c.name), warehouseColumnPaths);
193
202
  if (deletedColumnNames.length > 0 && !globalState_1.default.isNonInteractive()) {
194
203
  let answers = { isConfirm: assumeYes };
195
204
  if (!assumeYes) {
@@ -43,6 +43,85 @@ describe('Models', () => {
43
43
  expect((0, models_1.isGeneratableColumn)(catalog, ref, 'hits.page.pagePath')).toBe(false);
44
44
  });
45
45
  });
46
+ describe('getWarehouseTableForModel', () => {
47
+ const catalog = {
48
+ db: {
49
+ ds: {
50
+ orders: {
51
+ order_id: common_1.DimensionType.STRING,
52
+ product: common_1.DimensionType.STRING,
53
+ 'product.sku': common_1.DimensionType.STRING,
54
+ 'product.price': common_1.DimensionType.NUMBER,
55
+ 'product.variants': common_1.DimensionType.STRING,
56
+ 'product.variants.size': common_1.DimensionType.STRING,
57
+ customer: common_1.DimensionType.STRING,
58
+ 'customer.location': common_1.DimensionType.STRING,
59
+ tags: common_1.DimensionType.STRING,
60
+ },
61
+ },
62
+ },
63
+ };
64
+ Object.entries({
65
+ product: { repeated: true, record: true },
66
+ 'product.variants': { repeated: true, record: true },
67
+ customer: { repeated: false, record: true },
68
+ tags: { repeated: true, record: false },
69
+ }).forEach(([path, shape]) => (0, common_1.setCatalogNestedColumnShape)(catalog, 'db', 'ds', 'orders', path, shape));
70
+ const warehouseClient = {
71
+ getCatalog: vi.fn(async () => catalog),
72
+ };
73
+ const model = {
74
+ name: 'orders',
75
+ schema: 'ds',
76
+ database: 'db',
77
+ alias: 'orders',
78
+ originalFilePath: 'models/orders.sql',
79
+ patchPath: null,
80
+ packageName: 'p',
81
+ uniqueId: 'model.p.orders',
82
+ };
83
+ test('generates scalars, struct leaves and arrays of scalars but reports every warehouse path', async () => {
84
+ const result = await (0, models_1.getWarehouseTableForModel)({
85
+ model,
86
+ warehouseClient,
87
+ preserveColumnCase: false,
88
+ });
89
+ expect(Object.keys(result.table).sort()).toEqual([
90
+ 'customer.location',
91
+ 'order_id',
92
+ 'tags',
93
+ ]);
94
+ expect(result.warehouseColumnPaths.sort()).toEqual([
95
+ 'customer',
96
+ 'customer.location',
97
+ 'order_id',
98
+ 'product',
99
+ 'product.price',
100
+ 'product.sku',
101
+ 'product.variants',
102
+ 'product.variants.size',
103
+ 'tags',
104
+ ]);
105
+ });
106
+ });
107
+ describe('getColumnsMissingFromWarehouse', () => {
108
+ test('keeps documented nested leaves and containers, drops only paths the warehouse lacks', () => {
109
+ expect((0, models_1.getColumnsMissingFromWarehouse)([
110
+ 'order_id',
111
+ 'Product',
112
+ 'product.sku',
113
+ 'product.variants.size',
114
+ 'legacy_status',
115
+ 'product.colour',
116
+ ], [
117
+ 'order_id',
118
+ 'product',
119
+ 'product.sku',
120
+ 'product.variants',
121
+ 'product.variants.size',
122
+ ])).toEqual(['legacy_status', 'product.colour']);
123
+ });
124
+ });
46
125
  describe('isDocBlock', () => {
47
126
  test('should match all doc block variations', () => {
48
127
  expect((0, models_1.isDocBlock)("{{doc('user_id')}}")).toBe(true); // single quote
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/handlers/generate.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAMlD,KAAK,sBAAsB,GAAG,qBAAqB,GAAG;IAClD,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7B,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,kBAAkB,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,eAAe,YAAmB,sBAAsB,kBA6KpE,CAAC"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/handlers/generate.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAMlD,KAAK,sBAAsB,GAAG,qBAAqB,GAAG;IAClD,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7B,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,kBAAkB,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,eAAe,YAAmB,sBAAsB,kBA+KpE,CAAC"}
@@ -82,7 +82,7 @@ const generateHandler = async (options) => {
82
82
  for await (const compiledModel of compiledModels) {
83
83
  const spinner = globalState_1.default.startSpinner(` Generating .yml for model ${styles.bold(compiledModel.name)}`);
84
84
  try {
85
- const table = await (0, models_1.getWarehouseTableForModel)({
85
+ const { table, warehouseColumnPaths } = await (0, models_1.getWarehouseTableForModel)({
86
86
  model: compiledModel,
87
87
  warehouseClient,
88
88
  preserveColumnCase: options.preserveColumnCase,
@@ -95,6 +95,7 @@ const generateHandler = async (options) => {
95
95
  const { updatedYml, outputFilePath } = await (0, models_1.findAndUpdateModelYaml)({
96
96
  model: compiledModel,
97
97
  table,
98
+ warehouseColumnPaths,
98
99
  docs: manifest.docs,
99
100
  includeMeta: !options.excludeMeta,
100
101
  projectDir: absoluteProjectPath,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightdash/cli",
3
- "version": "2.209.2",
3
+ "version": "2.211.0",
4
4
  "description": "Lightdash CLI tool",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -22,8 +22,8 @@
22
22
  "@actions/core": "1.11.1",
23
23
  "@aws-sdk/credential-providers": "3.995.0",
24
24
  "@casl/ability": "6.8.0",
25
- "@lightdash/common": "2.209.2",
26
- "@lightdash/warehouses": "2.209.2",
25
+ "@lightdash/common": "2.211.0",
26
+ "@lightdash/warehouses": "2.211.0",
27
27
  "@sidvind/better-ajv-errors": "7.0.0",
28
28
  "@types/columnify": "1.5.1",
29
29
  "ajv": "8.18.0",