@apso/cli 0.1.0 → 0.1.4

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.
@@ -8,7 +8,7 @@ export default class Scaffold extends BaseCommand {
8
8
  };
9
9
  static args: {};
10
10
  scaffoldServer(dir: string, entity: Entity, relationshipMap: RelationshipMap, apiType: string): Promise<void>;
11
- setupRestFiles(dir: string, entity: Entity, relationshipMap: RelationshipMap): Promise<void>;
11
+ setupRestFiles(dir: string, entity: Entity, relationshipMap: RelationshipMap, apiType: string): Promise<void>;
12
12
  setupGraphqlFiles(dir: string, entity: Entity, relationshipMap: RelationshipMap): Promise<void>;
13
13
  run(): Promise<void>;
14
14
  }
@@ -18,18 +18,17 @@ class Scaffold extends base_command_1.default {
18
18
  await this.setupGraphqlFiles(dir, entity, relationshipMap);
19
19
  break;
20
20
  case apsorc_parser_1.ApiType.Rest:
21
- await this.setupRestFiles(dir, entity, relationshipMap);
21
+ await this.setupRestFiles(dir, entity, relationshipMap, apiType);
22
22
  break;
23
23
  default:
24
24
  break;
25
25
  }
26
- await (0, lib_1.createModule)(filePath, entityName, apiType);
26
+ await (0, lib_1.createModule)(filePath, entity, { apiType });
27
27
  }
28
- async setupRestFiles(dir, entity, relationshipMap) {
28
+ async setupRestFiles(dir, entity, relationshipMap, apiType) {
29
29
  const filePath = path.join(dir, entity.name);
30
- const entityRelationships = relationshipMap[entity.name] || [];
31
- await (0, lib_1.createDto)(filePath, entity, entityRelationships);
32
- await (0, lib_1.createService)(filePath, entity.name);
30
+ await (0, lib_1.createDto)(filePath, entity, { apiType });
31
+ await (0, lib_1.createService)(filePath, entity);
33
32
  await (0, lib_1.createController)(filePath, entity, relationshipMap);
34
33
  }
35
34
  async setupGraphqlFiles(dir, entity, relationshipMap) {
@@ -1,4 +1,5 @@
1
- import { ApsorcRelationship, Entity, RelationshipMap } from "./types";
1
+ import { Entity } from "./types/entity";
2
+ import { ApsorcRelationship, RelationshipMap } from "./types/relationship";
2
3
  export declare enum ApiType {
3
4
  Graphql = "graphql",
4
5
  Rest = "rest"
@@ -23,4 +24,29 @@ type ParsedApsorc = {
23
24
  export declare const parseApsorcV1: (apsorc: ApsorcType) => ParsedApsorcData;
24
25
  export declare const parseApsorcV2: (apsorc: ApsorcType) => ParsedApsorcData;
25
26
  export declare const parseApsorc: () => ParsedApsorc;
27
+ /**
28
+ * Represents the unified configuration structure after parsing and potential migration.
29
+ */
30
+ interface UnifiedConfig {
31
+ entities: Entity[];
32
+ relationships: ApsorcRelationship[];
33
+ rootFolder: string;
34
+ }
35
+ /**
36
+ * Finds the .apsorc configuration file path.
37
+ * Searches upwards from the current working directory.
38
+ * @returns The absolute path to the .apsorc file, or null if not found.
39
+ */
40
+ export declare const findConfigPath: () => string | null;
41
+ /**
42
+ * Loads, validates, and potentially migrates the .apsorc configuration.
43
+ * Uses the `rc` library to find and load the .apsorc file.
44
+ * Detects config version (V1 or V2) and migrates V1 to V2 if necessary.
45
+ * Performs basic validation on the final V2 structure.
46
+ * @param appName The name of the application (used by `rc`). Defaults to 'apso'.
47
+ * @param defaults Default configuration values (used by `rc`).
48
+ * @returns The unified configuration (entities, relationships, rootFolder).
49
+ * @throws Error if the config file cannot be found, is invalid, or migration fails.
50
+ */
51
+ export declare const loadConfig: (appName?: string, defaults?: {}) => UnifiedConfig;
26
52
  export {};
@@ -1,7 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseApsorc = exports.parseApsorcV2 = exports.parseApsorcV1 = exports.ApiType = void 0;
4
- const rc = require("rc");
3
+ exports.loadConfig = exports.findConfigPath = exports.parseApsorc = exports.parseApsorcV2 = exports.parseApsorcV1 = exports.ApiType = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const fs = tslib_1.__importStar(require("fs"));
6
+ const path = tslib_1.__importStar(require("path"));
7
+ const rc_1 = tslib_1.__importDefault(require("rc"));
5
8
  const relationships_1 = require("./utils/relationships");
6
9
  var ApiType;
7
10
  (function (ApiType) {
@@ -24,7 +27,7 @@ const parseApsorcV2 = (apsorc) => {
24
27
  };
25
28
  exports.parseApsorcV2 = parseApsorcV2;
26
29
  const parseRc = () => {
27
- const apsoConfig = rc("apso");
30
+ const apsoConfig = (0, rc_1.default)("apso");
28
31
  const rootFolder = apsoConfig.rootFolder || "src";
29
32
  const apiType = apsoConfig.apiType || "Rest";
30
33
  const version = apsoConfig.version || 1;
@@ -63,3 +66,94 @@ const parseApsorc = () => {
63
66
  }
64
67
  };
65
68
  exports.parseApsorc = parseApsorc;
69
+ /**
70
+ * Finds the .apsorc configuration file path.
71
+ * Searches upwards from the current working directory.
72
+ * @returns The absolute path to the .apsorc file, or null if not found.
73
+ */
74
+ const findConfigPath = () => {
75
+ let currentDir = process.cwd();
76
+ // Search upwards
77
+ while (currentDir !== path.parse(currentDir).root) {
78
+ const configPath = path.join(currentDir, ".apsorc");
79
+ if (fs.existsSync(configPath)) {
80
+ return configPath;
81
+ }
82
+ currentDir = path.dirname(currentDir);
83
+ }
84
+ // Check root directory as well
85
+ const rootConfigPath = path.join(currentDir, ".apsorc");
86
+ if (fs.existsSync(rootConfigPath)) {
87
+ return rootConfigPath;
88
+ }
89
+ return null; // Not found
90
+ };
91
+ exports.findConfigPath = findConfigPath;
92
+ /**
93
+ * Loads, validates, and potentially migrates the .apsorc configuration.
94
+ * Uses the `rc` library to find and load the .apsorc file.
95
+ * Detects config version (V1 or V2) and migrates V1 to V2 if necessary.
96
+ * Performs basic validation on the final V2 structure.
97
+ * @param appName The name of the application (used by `rc`). Defaults to 'apso'.
98
+ * @param defaults Default configuration values (used by `rc`).
99
+ * @returns The unified configuration (entities, relationships, rootFolder).
100
+ * @throws Error if the config file cannot be found, is invalid, or migration fails.
101
+ */
102
+ const loadConfig = (appName = "apso", defaults = {}) => {
103
+ // Use rc to find and load the configuration file (.apsorc)
104
+ const configData = (0, rc_1.default)(appName, defaults);
105
+ const rootFolder = configData.rootFolder || "src"; // Get rootFolder early
106
+ // Check if rc found and loaded the config
107
+ // rc returns an object with default keys if nothing is found,
108
+ // so check for more than just default keys or if configData.config exists
109
+ const hasMeaningfulConfig = configData &&
110
+ Object.keys(configData).some(key => !Object.prototype.hasOwnProperty.call(defaults, key) && key !== '_') &&
111
+ configData.configs && configData.configs.length > 0;
112
+ if (!hasMeaningfulConfig) {
113
+ // If rc didn't find it, try our manual search as a fallback
114
+ const foundPath = (0, exports.findConfigPath)();
115
+ // Use ternary to satisfy unicorn/prefer-ternary rule
116
+ throw foundPath
117
+ ? new Error(`Configuration file found at: ${foundPath}, but failed to parse. Ensure it is valid JSON or INI.`)
118
+ : new Error(".apsorc configuration file not found in the current directory or any parent directory.");
119
+ }
120
+ // Determine version and parse accordingly using existing functions
121
+ const version = configData.version || 1; // Default to 1 if undefined
122
+ let parsedData;
123
+ // Create an object matching ApsorcType structure from rc data
124
+ const apsorcInput = {
125
+ version: version,
126
+ rootFolder: rootFolder,
127
+ entities: configData.entities || [],
128
+ apiType: configData.apiType || ApiType.Rest,
129
+ relationships: configData.relationships || []
130
+ };
131
+ if (version === 1) {
132
+ // Pass the correctly structured object
133
+ parsedData = (0, exports.parseApsorcV1)(apsorcInput);
134
+ }
135
+ else if (version === 2 && Array.isArray(configData.relationships)) {
136
+ // Pass the correctly structured object
137
+ parsedData = (0, exports.parseApsorcV2)(apsorcInput);
138
+ }
139
+ else {
140
+ throw new Error("Invalid .apsorc format. Could not determine configuration version or structure.");
141
+ }
142
+ // Basic validation on the result of parsing
143
+ if (!parsedData.entities || !Array.isArray(parsedData.entities)) {
144
+ throw new Error("Invalid config: 'entities' array is missing or invalid after parsing.");
145
+ }
146
+ if (!rootFolder || typeof rootFolder !== 'string') {
147
+ throw new Error("Invalid config: 'rootFolder' string is missing or invalid.");
148
+ }
149
+ // Relationships are implicitly validated by parseRelationships/parseV1Relationships
150
+ return {
151
+ entities: parsedData.entities,
152
+ // Extract relationships from the relationshipMap keys/values if needed,
153
+ // or adjust UnifiedConfig. For now, returning empty array as placeholder.
154
+ // The relationshipMap is the primary output needed by downstream code.
155
+ relationships: [],
156
+ rootFolder: rootFolder
157
+ };
158
+ };
159
+ exports.loadConfig = loadConfig;
@@ -1,2 +1,10 @@
1
1
  import { Entity, RelationshipMap } from "./types";
2
+ /**
3
+ * Generates NestJS controller and spec files for a given entity.
4
+ *
5
+ * @param apiBaseDir The base directory for the generated API module (e.g., 'src/autogen/users').
6
+ * @param entity The entity definition object from the parsed .apsorc.
7
+ * @param relationshipMap The complete RelationshipMap generated from parsing all relationships.
8
+ * @returns {Promise<void>} A promise that resolves when the controller and spec files are created.
9
+ */
2
10
  export declare const createController: (apiBaseDir: string, entity: Entity, relationshipMap: RelationshipMap) => Promise<void>;
@@ -6,31 +6,46 @@ const Eta = tslib_1.__importStar(require("eta"));
6
6
  const path = tslib_1.__importStar(require("path"));
7
7
  const file_system_1 = require("./utils/file-system");
8
8
  const relationships_1 = require("./utils/relationships");
9
+ const pluralize_1 = tslib_1.__importDefault(require("pluralize"));
10
+ /**
11
+ * Generates NestJS controller and spec files for a given entity.
12
+ *
13
+ * @param apiBaseDir The base directory for the generated API module (e.g., 'src/autogen/users').
14
+ * @param entity The entity definition object from the parsed .apsorc.
15
+ * @param relationshipMap The complete RelationshipMap generated from parsing all relationships.
16
+ * @returns {Promise<void>} A promise that resolves when the controller and spec files are created.
17
+ */
9
18
  const createController = async (apiBaseDir, entity, relationshipMap) => {
10
19
  const { name: entityName } = entity;
11
- const File = path.join(apiBaseDir, `${entityName}.controller.ts`);
12
- const testFile = path.join(apiBaseDir, `${entityName}.controller.spec.ts`);
13
- // Dependencies
20
+ const controllerFileName = path.join(apiBaseDir, `${entityName}.controller.ts`);
21
+ const specFileName = path.join(apiBaseDir, `${entityName}.controller.spec.ts`);
22
+ // Dependencies and names needed by the template
14
23
  const svcName = `${entityName}Service`;
15
24
  const ctrlName = `${entityName}Controller`;
16
- const pluralEntityName = `${entityName}s`;
25
+ const pluralEntityName = (0, pluralize_1.default)(entityName);
26
+ // Get processed relationships specific to this entity
17
27
  const relationships = (0, relationships_1.getRelationshipForTemplate)(entityName, relationshipMap[entityName]);
28
+ // Get nested relationship paths for the @Crud join configuration
18
29
  const nestedRelationships = (0, relationships_1.getNestedRelationships)(entityName, relationshipMap);
30
+ // Data object for the Eta template
19
31
  const data = {
20
32
  svcName,
21
33
  ctrlName,
22
34
  entityName,
23
35
  pluralEntityName,
24
- // ensure uniqueness
36
+ // Ensure uniqueness in associations (though getRelationshipForTemplate might handle this)
25
37
  associations: [...new Set(relationships)],
26
38
  nestedAssociations: [...new Set(nestedRelationships)],
27
39
  };
40
+ // Configure Eta
28
41
  Eta.configure({
29
42
  views: path.join(__dirname, "templates"),
30
43
  });
31
- const content = await Eta.renderFileAsync("./rest/controller-rest", data);
32
- (0, file_system_1.createFile)(File, content);
33
- const testContent = await Eta.renderFileAsync("./rest/controller-rest-spec", data);
34
- (0, file_system_1.createFile)(testFile, testContent);
44
+ // Render controller template
45
+ const controllerContent = await Eta.renderFileAsync("./rest/controller-rest", data);
46
+ (0, file_system_1.createFile)(controllerFileName, controllerContent);
47
+ // Render spec template
48
+ const specContent = await Eta.renderFileAsync("./rest/controller-rest-spec", data);
49
+ (0, file_system_1.createFile)(specFileName, specContent);
35
50
  };
36
51
  exports.createController = createController;
package/dist/lib/dto.d.ts CHANGED
@@ -1,7 +1,13 @@
1
- import { Entity, Relationship } from "./types";
2
- export interface ComputedField {
3
- name: string;
4
- primary?: boolean;
5
- dataType: string;
6
- }
7
- export declare const createDto: (apiBaseDir: string, entity: Entity, relationships: Relationship[]) => Promise<void>;
1
+ import { Entity } from "./types/entity";
2
+ /**
3
+ * Generates Data Transfer Object (DTO) files (create and update) for a given entity.
4
+ *
5
+ * @param apiBaseDir The base directory for the generated API module (e.g., 'src/autogen/users').
6
+ * @param entity The entity definition object from the parsed .apsorc.
7
+ * @param options Optional configuration flags.
8
+ * @param options.apiType The type of API being generated (default: "rest").
9
+ * @returns {Promise<void>} A promise that resolves when the DTO files are created.
10
+ */
11
+ export declare const createDto: (apiBaseDir: string, entity: Entity, options?: {
12
+ apiType?: string;
13
+ }) => Promise<void>;
package/dist/lib/dto.js CHANGED
@@ -7,43 +7,51 @@ const path = tslib_1.__importStar(require("path"));
7
7
  const file_system_1 = require("./utils/file-system");
8
8
  const field_1 = require("./utils/field");
9
9
  const relationships_1 = require("./utils/relationships");
10
- const createDto = async (apiBaseDir, entity, relationships) => {
11
- const { name, fields = [] } = entity;
12
- const Dir = path.join(apiBaseDir, "dtos");
13
- const File = path.join(Dir, `${name}.dto.ts`);
14
- const relationshipFields = relationships
15
- .filter((relationship) => relationship.type === "ManyToOne")
16
- .map((relationship) => (0, relationships_1.getRelationshipIdField)(relationship));
17
- const columns = [
18
- ...fields.map((field) => ({
19
- name: field.name,
20
- type: field.type,
21
- primary: field.primary,
22
- dataType: field.type === "enum"
23
- ? (0, field_1.fieldToEnumType)(field.name, name)
24
- : (0, field_1.getJsTypeFromFieldType)(field.type),
25
- })),
26
- ...relationshipFields.map((fieldName) => ({
27
- name: fieldName,
28
- dataType: "number",
29
- })),
30
- ];
10
+ const casing_1 = require("./utils/casing");
11
+ /**
12
+ * Generates Data Transfer Object (DTO) files (create and update) for a given entity.
13
+ *
14
+ * @param apiBaseDir The base directory for the generated API module (e.g., 'src/autogen/users').
15
+ * @param entity The entity definition object from the parsed .apsorc.
16
+ * @param options Optional configuration flags.
17
+ * @param options.apiType The type of API being generated (default: "rest").
18
+ * @returns {Promise<void>} A promise that resolves when the DTO files are created.
19
+ */
20
+ const createDto = async (apiBaseDir, entity, options) => {
21
+ var _a;
22
+ const { name: entityName, fields = [], associations } = entity;
23
+ const dtoDir = path.join(apiBaseDir, "dto");
24
+ const dtoFile = path.join(dtoDir, `${entityName}.dto.ts`);
25
+ const columns = (0, field_1.getFieldForTemplate)(fields, entityName);
26
+ const relationshipsTemplate = (0, relationships_1.getRelationshipForTemplate)(entityName, associations || []);
31
27
  const primaryKeyColumns = columns.filter((column) => column.primary === true);
32
28
  const addDefaultPKProperty = primaryKeyColumns.length === 0;
33
29
  const createdAt = entity.created_at;
34
30
  const updatedAt = entity.updated_at;
31
+ // Filter columns: remove id, createdAt, updatedAt
32
+ const dtoColumns = columns.filter((col) => !["id", "createdAt", "updatedAt"].includes(col.name));
33
+ // Class names
34
+ const createDtoName = `Create${(0, casing_1.camelCase)(entityName, true)}Dto`;
35
+ const updateDtoName = `Update${(0, casing_1.camelCase)(entityName, true)}Dto`;
35
36
  const data = {
36
- name: entity.name,
37
+ entityName,
37
38
  addDefaultPKProperty,
38
39
  createdAt,
39
40
  updatedAt,
40
- columns,
41
+ columns: dtoColumns,
42
+ associations: relationshipsTemplate,
43
+ createDtoName,
44
+ updateDtoName,
45
+ apiType: (_a = options === null || options === void 0 ? void 0 : options.apiType) !== null && _a !== void 0 ? _a : "rest",
41
46
  importEnums: (0, field_1.typeExistsInEntity)(entity, "enum") !== -1,
42
47
  };
48
+ // Configure Eta
43
49
  Eta.configure({
44
50
  views: path.join(__dirname, "templates"),
45
51
  });
46
- const content = await Eta.renderFileAsync("./rest/dto-rest", data);
47
- (0, file_system_1.createFile)(File, content);
52
+ // Render the unified template only once
53
+ const dtoContent = await Eta.renderFileAsync("./rest/dto-rest", data);
54
+ // Create only one DTO file
55
+ (0, file_system_1.createFile)(dtoFile, dtoContent);
48
56
  };
49
57
  exports.createDto = createDto;
@@ -1,2 +1,11 @@
1
1
  import { Entity, Relationship } from "./types";
2
+ /**
3
+ * Generates a TypeORM entity file based on the entity definition and relationships.
4
+ *
5
+ * @param apiBaseDir The base directory for the generated API module (e.g., 'src/autogen/users').
6
+ * @param entity The entity definition object from the parsed .apsorc.
7
+ * @param relationshipsNew An array of processed Relationship objects for this entity.
8
+ * @param apiType The type of API being generated (e.g., "rest", "graphql").
9
+ * @returns {Promise<void>} A promise that resolves when the entity file is created.
10
+ */
2
11
  export declare const createEntity: (apiBaseDir: string, entity: Entity, relationshipsNew: Relationship[], apiType: string) => Promise<void>;
@@ -4,11 +4,20 @@ exports.createEntity = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const Eta = tslib_1.__importStar(require("eta"));
6
6
  const path = tslib_1.__importStar(require("path"));
7
- const pluralize_1 = tslib_1.__importDefault(require("pluralize"));
8
7
  const file_system_1 = require("./utils/file-system");
9
- const casing_1 = require("./utils/casing");
10
8
  const field_1 = require("./utils/field");
11
9
  const relationships_1 = require("./utils/relationships");
10
+ const casing_1 = require("./utils/casing");
11
+ const pluralize_1 = tslib_1.__importDefault(require("pluralize"));
12
+ /**
13
+ * Generates a TypeORM entity file based on the entity definition and relationships.
14
+ *
15
+ * @param apiBaseDir The base directory for the generated API module (e.g., 'src/autogen/users').
16
+ * @param entity The entity definition object from the parsed .apsorc.
17
+ * @param relationshipsNew An array of processed Relationship objects for this entity.
18
+ * @param apiType The type of API being generated (e.g., "rest", "graphql").
19
+ * @returns {Promise<void>} A promise that resolves when the entity file is created.
20
+ */
12
21
  const createEntity = async (apiBaseDir, entity, relationshipsNew, apiType) => {
13
22
  const { name, fields = [], indexes, uniques } = entity;
14
23
  const File = path.join(apiBaseDir, `${name}.entity.ts`);
@@ -37,7 +46,8 @@ const createEntity = async (apiBaseDir, entity, relationshipsNew, apiType) => {
37
46
  Eta.configure({
38
47
  views: path.join(__dirname, "templates"),
39
48
  });
40
- const content = await Eta.renderFileAsync("./entities/entity", data);
49
+ const templatePath = apiType === "graphql" ? "./graphql/gql-entity-graphql" : "./entities/entity";
50
+ const content = await Eta.renderFileAsync(templatePath, data);
41
51
  (0, file_system_1.createFile)(File, content);
42
52
  };
43
53
  exports.createEntity = createEntity;
@@ -1,2 +1,13 @@
1
1
  import { Entity } from "./types";
2
+ /**
3
+ * Generates an index file (e.g., index.ts) that exports all generated entity modules.
4
+ *
5
+ * This file serves as a central point for importing and managing the generated modules
6
+ * within the application, tailored to the specified API type (REST or GraphQL).
7
+ *
8
+ * @param apiBaseDir The base directory where the generated API modules reside (e.g., 'src/autogen').
9
+ * @param entities An array of Entity definition objects, used to determine which modules to export.
10
+ * @param apiType The type of API being generated ('rest' or 'graphql'), which determines the template used.
11
+ * @returns {Promise<void>} A promise that resolves when the index file is created.
12
+ */
2
13
  export declare const createIndexAppModule: (apiBaseDir: string, entities: Entity[], apiType: string) => Promise<void>;
@@ -5,6 +5,17 @@ const tslib_1 = require("tslib");
5
5
  const file_system_1 = require("./utils/file-system");
6
6
  const Eta = tslib_1.__importStar(require("eta"));
7
7
  const path = tslib_1.__importStar(require("path"));
8
+ /**
9
+ * Generates an index file (e.g., index.ts) that exports all generated entity modules.
10
+ *
11
+ * This file serves as a central point for importing and managing the generated modules
12
+ * within the application, tailored to the specified API type (REST or GraphQL).
13
+ *
14
+ * @param apiBaseDir The base directory where the generated API modules reside (e.g., 'src/autogen').
15
+ * @param entities An array of Entity definition objects, used to determine which modules to export.
16
+ * @param apiType The type of API being generated ('rest' or 'graphql'), which determines the template used.
17
+ * @returns {Promise<void>} A promise that resolves when the index file is created.
18
+ */
8
19
  const createIndexAppModule = async (apiBaseDir, entities, apiType) => {
9
20
  const File = path.join(apiBaseDir, `index.ts`);
10
21
  Eta.configure({
@@ -1 +1,13 @@
1
- export declare const createModule: (apiBaseDir: string, entityName: string, apiType: string) => Promise<void>;
1
+ import { Entity } from "./types";
2
+ /**
3
+ * Generates a NestJS module file for a given entity.
4
+ *
5
+ * @param apiBaseDir The base directory for the generated API module (e.g., 'src/autogen/users').
6
+ * @param entity The entity definition object from the parsed .apsorc.
7
+ * @param options Optional configuration flags.
8
+ * @param options.apiType The type of API being generated (default: "rest").
9
+ * @returns {Promise<void>} A promise that resolves when the module file is created.
10
+ */
11
+ export declare const createModule: (apiBaseDir: string, entity: Entity, options?: {
12
+ apiType?: string;
13
+ }) => Promise<void>;
@@ -5,24 +5,40 @@ const tslib_1 = require("tslib");
5
5
  const Eta = tslib_1.__importStar(require("eta"));
6
6
  const path = tslib_1.__importStar(require("path"));
7
7
  const file_system_1 = require("./utils/file-system");
8
- const createModule = async (apiBaseDir, entityName, apiType) => {
9
- const File = path.join(apiBaseDir, `${entityName}.module.ts`);
10
- // Dependencies
8
+ /**
9
+ * Generates a NestJS module file for a given entity.
10
+ *
11
+ * @param apiBaseDir The base directory for the generated API module (e.g., 'src/autogen/users').
12
+ * @param entity The entity definition object from the parsed .apsorc.
13
+ * @param options Optional configuration flags.
14
+ * @param options.apiType The type of API being generated (default: "rest").
15
+ * @returns {Promise<void>} A promise that resolves when the module file is created.
16
+ */
17
+ const createModule = async (apiBaseDir, entity, options) => {
18
+ const { name: entityName } = entity;
19
+ const moduleFileName = path.join(apiBaseDir, `${entityName}.module.ts`);
20
+ // Names needed by the templates
21
+ const moduleName = `${entityName}Module`;
11
22
  const svcName = `${entityName}Service`;
12
- const ctrlName = `${entityName}Controller`;
13
- const modName = `${entityName}Module`;
14
- const pluralEntityName = `${entityName}s`;
23
+ const ctrlName = `${entityName}Controller`; // Used by REST template
24
+ const resolverName = `${entityName}Resolver`; // Used by GQL template
25
+ // Data for the Eta templates
15
26
  const data = {
27
+ moduleName,
16
28
  svcName,
17
29
  ctrlName,
18
- modName,
30
+ resolverName,
19
31
  entityName,
20
- pluralEntityName,
21
32
  };
33
+ // Configure Eta
22
34
  Eta.configure({
23
35
  views: path.join(__dirname, "templates"),
24
36
  });
25
- const content = await Eta.renderFileAsync(`./${apiType}/module-${apiType}`, data);
26
- (0, file_system_1.createFile)(File, content);
37
+ // Determine template based on API type
38
+ const templatePath = (options === null || options === void 0 ? void 0 : options.apiType) === "graphql" ? "./graphql/gql-module-graphql" : "./rest/module-rest";
39
+ // Render the template
40
+ const content = await Eta.renderFileAsync(templatePath, data);
41
+ // Create the module file
42
+ (0, file_system_1.createFile)(moduleFileName, content);
27
43
  };
28
44
  exports.createModule = createModule;
@@ -1 +1,9 @@
1
- export declare const createService: (apiBaseDir: string, entityName: string) => Promise<void>;
1
+ import { Entity } from "./types";
2
+ /**
3
+ * Generates NestJS service and spec files for a given entity.
4
+ *
5
+ * @param apiBaseDir The base directory for the generated API module (e.g., 'src/autogen/users').
6
+ * @param entity The entity definition object from the parsed .apsorc.
7
+ * @returns {Promise<void>} A promise that resolves when the service and spec files are created.
8
+ */
9
+ export declare const createService: (apiBaseDir: string, entity: Entity) => Promise<void>;
@@ -5,20 +5,35 @@ const tslib_1 = require("tslib");
5
5
  const file_system_1 = require("./utils/file-system");
6
6
  const Eta = tslib_1.__importStar(require("eta"));
7
7
  const path = tslib_1.__importStar(require("path"));
8
- const createService = async (apiBaseDir, entityName) => {
9
- const File = path.join(apiBaseDir, `${entityName}.service.ts`);
10
- // Dependencies
8
+ /**
9
+ * Generates NestJS service and spec files for a given entity.
10
+ *
11
+ * @param apiBaseDir The base directory for the generated API module (e.g., 'src/autogen/users').
12
+ * @param entity The entity definition object from the parsed .apsorc.
13
+ * @returns {Promise<void>} A promise that resolves when the service and spec files are created.
14
+ */
15
+ const createService = async (apiBaseDir, entity) => {
16
+ const { name: entityName } = entity;
17
+ const serviceFileName = path.join(apiBaseDir, `${entityName}.service.ts`);
18
+ const specFileName = path.join(apiBaseDir, `${entityName}.service.spec.ts`);
19
+ // Names needed by the templates
11
20
  const svcName = `${entityName}Service`;
12
- const pluralEntityName = `${entityName}s`;
21
+ const repoName = `${entityName}Repository`; // Assuming convention
22
+ // Data for the Eta templates
13
23
  const data = {
14
24
  svcName,
25
+ repoName,
15
26
  entityName,
16
- pluralEntityName,
17
27
  };
28
+ // Configure Eta
18
29
  Eta.configure({
19
30
  views: path.join(__dirname, "templates"),
20
31
  });
21
- const content = await Eta.renderFileAsync("./rest/service-rest", data);
22
- (0, file_system_1.createFile)(File, content);
32
+ // Render service template
33
+ const serviceContent = await Eta.renderFileAsync("./rest/service-rest", data);
34
+ (0, file_system_1.createFile)(serviceFileName, serviceContent);
35
+ // Render spec template
36
+ const specContent = await Eta.renderFileAsync("./rest/service-rest-spec", data);
37
+ (0, file_system_1.createFile)(specFileName, specContent);
23
38
  };
24
39
  exports.createService = createService;
@@ -0,0 +1,38 @@
1
+ import { Test, TestingModule } from '@nestjs/testing';
2
+ import { <%= it.svcName %> } from './<%= it.entityName %>.service';
3
+ import { getRepositoryToken } from '@nestjs/typeorm';
4
+ import { <%= it.entityName %> } from './<%= it.entityName %>.entity';
5
+
6
+ describe('<%= it.svcName %>', () => {
7
+ let service: <%= it.svcName %>;
8
+ let mockRepository: any;
9
+
10
+ beforeEach(async () => {
11
+ mockRepository = {
12
+ find: jest.fn().mockResolvedValue([]),
13
+ findOne: jest.fn().mockResolvedValue({}),
14
+ save: jest.fn().mockResolvedValue({}),
15
+ create: jest.fn().mockReturnValue({}),
16
+ update: jest.fn().mockResolvedValue({}),
17
+ delete: jest.fn().mockResolvedValue({}),
18
+ };
19
+
20
+ const module: TestingModule = await Test.createTestingModule({
21
+ providers: [
22
+ <%= it.svcName %>,
23
+ {
24
+ provide: getRepositoryToken(<%= it.entityName %>),
25
+ useValue: mockRepository,
26
+ },
27
+ ],
28
+ }).compile();
29
+
30
+ service = module.get<<%= it.svcName %>>(<%= it.svcName %>);
31
+ });
32
+
33
+ it('should be defined', () => {
34
+ expect(service).toBeDefined();
35
+ });
36
+
37
+ // Add more tests as needed
38
+ });