@olenbetong/appframe-vite 6.17.0 → 6.17.1

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.
@@ -3,7 +3,7 @@ import { authenticate } from "@olenbetong/appframe-auth";
3
3
  import { Client, generateApiDataHandler } from "@olenbetong/appframe-data";
4
4
  import { config } from "dotenv";
5
5
  import { getAppHostname } from "./devServer.js";
6
- import { fetchAndGenerate, writeGeneratedFile } from "./resourceGenerate.js";
6
+ import { fetchAndGenerate, getUniqueDefinitionName, writeGeneratedFile } from "./resourceGenerate.js";
7
7
  import { entryToCLIOptions, readResourcesConfig, writeResourcesConfig, } from "./resourcesConfig.js";
8
8
  config({ quiet: true });
9
9
  /** Views holding the CMS article configuration. They are registered on demand. */
@@ -155,8 +155,10 @@ function dataObjectToEntry(datasource, fields, outputDir, warnings) {
155
155
  entry.maxRecords = datasource.MaxRecords;
156
156
  if (datasource.WhereClause)
157
157
  entry.where = datasource.WhereClause;
158
- if (datasource.UniqueTableName && datasource.UniqueTableName !== datasource.ViewName) {
159
- entry.unique = datasource.UniqueTableName;
158
+ // A table as the unique table means its security view: the data API saves through views.
159
+ let unique = datasource.UniqueTableName ? getUniqueDefinitionName(datasource.UniqueTableName) : null;
160
+ if (unique && unique !== datasource.ViewName) {
161
+ entry.unique = unique;
160
162
  }
161
163
  if (datasource.DynamicLoading)
162
164
  entry.dynamic = true;
@@ -55,6 +55,11 @@ export declare function parseYamlConfig(fileContent: string): {
55
55
  export declare function getCustomImportPath(outputPath: string): string;
56
56
  export declare function getProcedureDefinition(name: string, procDefinition: any, options: CLIOptions): string;
57
57
  export declare function getDataObjectDefinition(name: string, viewDefinition: any, options: CLIOptions): string;
58
+ /**
59
+ * The object to read a unique table's columns from. Tables are not data API resources, so a
60
+ * table (`atbl_`) is read through its security view (`atbv_`), which has the same columns.
61
+ */
62
+ export declare function getUniqueDefinitionName(unique: string): string;
58
63
  /**
59
64
  * Fetch the resource definition from an authenticated Appframe client.
60
65
  */
@@ -373,6 +373,11 @@ export function getDataObjectDefinition(name, viewDefinition, options) {
373
373
  }
374
374
  if (aggregates[field.Name]) {
375
375
  fieldDefinition.aggregate = aggregates[field.Name];
376
+ // A count is a number whatever the column it counts, and never null.
377
+ if (/^COUNT/i.test(aggregates[field.Name])) {
378
+ fieldDefinition.type = "int";
379
+ fieldDefinition.nullable = false;
380
+ }
376
381
  }
377
382
  fields.push(fieldDefinition);
378
383
  }
@@ -520,6 +525,13 @@ ${fieldTypes}}`;
520
525
  // ---------------------------------------------------------------------------
521
526
  // Server interaction
522
527
  // ---------------------------------------------------------------------------
528
+ /**
529
+ * The object to read a unique table's columns from. Tables are not data API resources, so a
530
+ * table (`atbl_`) is read through its security view (`atbv_`), which has the same columns.
531
+ */
532
+ export function getUniqueDefinitionName(unique) {
533
+ return unique.startsWith("atbl_") ? `atbv_${unique.slice(5)}` : unique;
534
+ }
523
535
  /**
524
536
  * Fetch the resource definition from an authenticated Appframe client.
525
537
  */
@@ -567,11 +579,11 @@ export async function fetchAndGenerate(resourceName, options, client) {
567
579
  // lacks. CMS data objects know to leave those out of a save; mark them ignored so these do too.
568
580
  if (definition.ObjectType === "V" && options.unique) {
569
581
  try {
570
- let uniqueDefinition = await fetchResourceDefinition(client, options.unique);
582
+ let uniqueDefinition = await fetchResourceDefinition(client, getUniqueDefinitionName(options.unique));
571
583
  options.uniqueFields = (uniqueDefinition.Parameters ?? []).map((p) => p.Name);
572
584
  }
573
585
  catch (error) {
574
- console.warn(`Could not read the unique table '${options.unique}' for '${resourceName}', so no fields are marked ignored: ${error instanceof Error ? error.message.split(" — ")[0] : error}`);
586
+ console.warn(`Could not read the unique table '${getUniqueDefinitionName(options.unique)}' for '${resourceName}', so no fields are marked ignored: ${error instanceof Error ? error.message.split(" — ")[0] : error}`);
575
587
  }
576
588
  }
577
589
  return definition.ObjectType === "V"
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { getDataObjectDefinition } from "./resourceGenerate.js";
2
+ import { getDataObjectDefinition, getUniqueDefinitionName } from "./resourceGenerate.js";
3
3
  import { entryToCLIOptions } from "./resourcesConfig.js";
4
4
  const definition = {
5
5
  ObjectType: "V",
@@ -31,3 +31,28 @@ describe("fields outside the unique table", () => {
31
31
  expect(generate(undefined)).not.toContain('"ignored"');
32
32
  });
33
33
  });
34
+ describe("getUniqueDefinitionName", () => {
35
+ it("reads a table through its security view", () => {
36
+ expect(getUniqueDefinitionName("atbl_Production_Plans")).toBe("atbv_Production_Plans");
37
+ });
38
+ it("keeps views as they are", () => {
39
+ expect(getUniqueDefinitionName("atbv_Production_Plans")).toBe("atbv_Production_Plans");
40
+ expect(getUniqueDefinitionName("atbx_Accounting_Projects")).toBe("atbx_Accounting_Projects");
41
+ });
42
+ });
43
+ describe("count aggregates", () => {
44
+ it("types a counted column as a number", () => {
45
+ let options = entryToCLIOptions({
46
+ id: "dsSummary",
47
+ resource: "aviw_Production_Plans",
48
+ output: "src/dsSummary.ts",
49
+ global: true,
50
+ types: true,
51
+ groupBy: ["ProjectID"],
52
+ aggregates: [{ field: "ProjectName", aggregate: "COUNT" }],
53
+ }, "example.com");
54
+ let output = getDataObjectDefinition("aviw_Production_Plans", definition, options);
55
+ expect(output).toContain("ProjectName: number;");
56
+ expect(output).toMatch(/"name": "ProjectName",\s*"type": "number"/);
57
+ });
58
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olenbetong/appframe-vite",
3
- "version": "6.17.0",
3
+ "version": "6.17.1",
4
4
  "description": "Tools to use and deploy Vite applications to Appframe",
5
5
  "main": "./lib/index.js",
6
6
  "type": "module",
@@ -31,11 +31,11 @@
31
31
  "dependencies": {
32
32
  "@olenbetong/appframe-auth": "^0.1.0",
33
33
  "@olenbetong/appframe-data": "^1.8.4",
34
- "body-parser": "^2.3.0",
34
+ "body-parser": "^2.4.0",
35
35
  "chalk": "6.0.1",
36
36
  "chokidar": "^5.0.0",
37
37
  "commander": "15.0.0",
38
- "dotenv": "^18.0.8",
38
+ "dotenv": "^18.0.9",
39
39
  "fuzzy": "^0.1.3",
40
40
  "inquirer": "^14.2.4",
41
41
  "inquirer-autocomplete-standalone": "^0.8.1",