@solidxai/core 0.1.6-beta.13 → 0.1.6-beta.15
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.
- package/.claude/settings.local.json +15 -0
- package/dist/entities/mq-message.entity.d.ts.map +1 -1
- package/dist/entities/mq-message.entity.js +1 -0
- package/dist/entities/mq-message.entity.js.map +1 -1
- package/dist/helpers/bootstrap.helper.d.ts +14 -0
- package/dist/helpers/bootstrap.helper.d.ts.map +1 -0
- package/dist/helpers/bootstrap.helper.js +132 -0
- package/dist/helpers/bootstrap.helper.js.map +1 -0
- package/dist/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.d.ts +2 -2
- package/dist/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.js +8 -5
- package/dist/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +1 -0
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/services/computed-fields/entity/entity-id-sequence-num-computed-field-provider.d.ts +15 -0
- package/dist/services/computed-fields/entity/entity-id-sequence-num-computed-field-provider.d.ts.map +1 -0
- package/dist/services/computed-fields/entity/entity-id-sequence-num-computed-field-provider.js +71 -0
- package/dist/services/computed-fields/entity/entity-id-sequence-num-computed-field-provider.js.map +1 -0
- package/dist/services/crud.service.js +1 -1
- package/dist/services/crud.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +2 -0
- package/dist/solid-core.module.js.map +1 -1
- package/package.json +3 -1
- package/src/entities/mq-message.entity.ts +1 -0
- package/src/helpers/bootstrap.helper.ts +222 -0
- package/src/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.ts +9 -6
- package/src/index.ts +1 -0
- package/src/interfaces.ts +2 -0
- package/src/services/1.js +6 -0
- package/src/services/computed-fields/entity/entity-id-sequence-num-computed-field-provider.ts +70 -0
- package/src/services/crud.service.ts +1 -1
- package/src/solid-core.module.ts +3 -0
- package/dist-tests/api/authenticate.spec.js +0 -119
- package/dist-tests/api/authenticate.spec.js.map +0 -1
- package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +0 -97
- package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +0 -1
- package/dist-tests/api/ping.spec.js +0 -21
- package/dist-tests/api/ping.spec.js.map +0 -1
- package/dist-tests/helpers/auth.js +0 -41
- package/dist-tests/helpers/auth.js.map +0 -1
- package/dist-tests/helpers/env.js +0 -11
- package/dist-tests/helpers/env.js.map +0 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { classify } from "@angular-devkit/core/src/utils/strings";
|
|
2
|
+
import { Injectable } from "@nestjs/common";
|
|
3
|
+
import { InjectDataSource } from "@nestjs/typeorm";
|
|
4
|
+
import { ComputedFieldTriggerOperation } from "src/dtos/create-field-metadata.dto";
|
|
5
|
+
import { ComputedFieldProvider } from "src/decorators/computed-field-provider.decorator";
|
|
6
|
+
import { CommonEntity } from "src/entities/common.entity";
|
|
7
|
+
import { ModelSequence } from "src/entities/model-sequence.entity";
|
|
8
|
+
import { ComputedFieldMetadata } from "src/helpers/solid-registry";
|
|
9
|
+
import { IEntityPostComputeFieldProvider } from "src/interfaces";
|
|
10
|
+
import { DataSource } from "typeorm";
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export interface EntityIdSequenceNumComputedFieldContext {
|
|
14
|
+
sequenceName: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@ComputedFieldProvider()
|
|
18
|
+
@Injectable()
|
|
19
|
+
export class EntityIdSequenceNumComputedFieldProvider<T extends CommonEntity> implements IEntityPostComputeFieldProvider<T, EntityIdSequenceNumComputedFieldContext> {
|
|
20
|
+
constructor(
|
|
21
|
+
@InjectDataSource()
|
|
22
|
+
private readonly dataSource: DataSource
|
|
23
|
+
) { }
|
|
24
|
+
|
|
25
|
+
name(): string {
|
|
26
|
+
return "EntityIdSequenceNumComputedFieldProvider";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
help(): string {
|
|
30
|
+
return "Computed field provider used to create fields whose value is based on prefix, padding & the entity's own id. Intended for use in after-insert hooks only.";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async postComputeAndSaveValue(triggerEntity: T, computedFieldMetadata: ComputedFieldMetadata<EntityIdSequenceNumComputedFieldContext>): Promise<void> {
|
|
34
|
+
const eventType = computedFieldMetadata.eventContext?.eventType;
|
|
35
|
+
if (eventType !== ComputedFieldTriggerOperation.afterInsert) {
|
|
36
|
+
throw new Error(`EntityIdSequenceNumComputedFieldProvider only supports "${ComputedFieldTriggerOperation.afterInsert}" events, but received "${eventType}"`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const { sequenceName } = computedFieldMetadata.computedFieldValueProviderCtxt ?? {};
|
|
40
|
+
|
|
41
|
+
if (!sequenceName) {
|
|
42
|
+
throw new Error("sequenceName is required for EntityIdSequenceNum computation");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const modelSequenceRepo = this.dataSource.manager.getRepository(ModelSequence);
|
|
46
|
+
const modelSequence = await modelSequenceRepo.findOne({
|
|
47
|
+
where: { sequenceName },
|
|
48
|
+
relations: { model: true },
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (!modelSequence) {
|
|
52
|
+
throw new Error(`ModelSequence not found for ${sequenceName}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const modelSingularName = modelSequence.model?.singularName;
|
|
56
|
+
if (!modelSingularName) {
|
|
57
|
+
throw new Error(`Model singularName not found for sequence ${sequenceName}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const padding = modelSequence.padding ?? 5;
|
|
61
|
+
const prefix = modelSequence.prefix ?? "";
|
|
62
|
+
const separator = modelSequence.separator ?? "";
|
|
63
|
+
const paddedId = String(triggerEntity.id).padStart(padding, "0");
|
|
64
|
+
const sequenceString = `${prefix}${separator}${paddedId}`;
|
|
65
|
+
|
|
66
|
+
const entityName = classify(modelSingularName);
|
|
67
|
+
const entityRepo = this.dataSource.manager.getRepository(entityName);
|
|
68
|
+
await entityRepo.update(triggerEntity.id, { [computedFieldMetadata.fieldName]: sequenceString });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -432,7 +432,7 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
|
|
|
432
432
|
// dataSource: string; // The name of the selection provider
|
|
433
433
|
// filterSchema : json // This is a custom json object that every data source will handle accordingly. We could validate the query against the selection provider
|
|
434
434
|
// values : string[]; // The values returned by the selection provider
|
|
435
|
-
const options = { ...commonOptions, selectionDynamicProvider: fieldMetadata.selectionDynamicProvider, selectionDynamicProviderCtxt: fieldMetadata.selectionDynamicProviderCtxt, selectionValueType: fieldMetadata.selectionValueType as SelectionValueType, discoveryService: this.discoveryService, isMultiSelect: fieldMetadata.isMultiSelect };
|
|
435
|
+
const options = { ...commonOptions, selectionDynamicProvider: fieldMetadata.selectionDynamicProvider, selectionDynamicProviderCtxt: JSON.parse(fieldMetadata.selectionDynamicProviderCtxt), selectionValueType: fieldMetadata.selectionValueType as SelectionValueType, discoveryService: this.discoveryService, isMultiSelect: fieldMetadata.isMultiSelect };
|
|
436
436
|
return new SelectionDynamicFieldCrudManager(options);
|
|
437
437
|
}
|
|
438
438
|
case SolidFieldType.uuid: {
|
package/src/solid-core.module.ts
CHANGED
|
@@ -337,6 +337,8 @@ import { ImageEncodingService } from './helpers/image-encoding.helper';
|
|
|
337
337
|
import { SolidMicroserviceAdapter } from './helpers/solid-microservice-adapter.service';
|
|
338
338
|
import { InfoCommand } from './commands/info.command';
|
|
339
339
|
import { ListOfRolesSelectionProvider } from './services/selection-providers/list-of-roles-selectionproviders.service';
|
|
340
|
+
import { Entity } from 'typeorm';
|
|
341
|
+
import { EntityIdSequenceNumComputedFieldProvider } from './services/computed-fields/entity/entity-id-sequence-num-computed-field-provider';
|
|
340
342
|
|
|
341
343
|
|
|
342
344
|
@Global()
|
|
@@ -720,6 +722,7 @@ import { ListOfRolesSelectionProvider } from './services/selection-providers/lis
|
|
|
720
722
|
FixturesTearDownCommand,
|
|
721
723
|
DatabaseBootstrapService,
|
|
722
724
|
SequenceNumComputedFieldProvider,
|
|
725
|
+
EntityIdSequenceNumComputedFieldProvider,
|
|
723
726
|
ModelSequenceService,
|
|
724
727
|
ModelSequenceRepository,
|
|
725
728
|
SolidCoreDefaultSettingsProvider,
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const test_1 = require("@playwright/test");
|
|
4
|
-
const env_1 = require("../helpers/env");
|
|
5
|
-
const baseURL = process.env.API_BASE_URL ?? "http://localhost:3000";
|
|
6
|
-
const TEST_USER_EMAIL = (0, env_1.getRequiredEnv)("TEST_USER_EMAIL");
|
|
7
|
-
const TEST_USER_PASSWORD = (0, env_1.getRequiredEnv)("TEST_USER_PASSWORD");
|
|
8
|
-
function base64UrlDecode(input) {
|
|
9
|
-
const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
10
|
-
const padded = normalized.length % 4 === 0
|
|
11
|
-
? normalized
|
|
12
|
-
: normalized.padEnd(normalized.length + (4 - (normalized.length % 4)), "=");
|
|
13
|
-
return Buffer.from(padded, "base64").toString("utf-8");
|
|
14
|
-
}
|
|
15
|
-
function validateJwt(token) {
|
|
16
|
-
const parts = token.split(".");
|
|
17
|
-
if (parts.length !== 3) {
|
|
18
|
-
throw new Error("JWT must have three dot-separated parts.");
|
|
19
|
-
}
|
|
20
|
-
const headerJson = JSON.parse(base64UrlDecode(parts[0]));
|
|
21
|
-
const payloadJson = JSON.parse(base64UrlDecode(parts[1]));
|
|
22
|
-
if (!headerJson || typeof headerJson !== "object") {
|
|
23
|
-
throw new Error("JWT header must be a JSON object.");
|
|
24
|
-
}
|
|
25
|
-
if (!payloadJson || typeof payloadJson !== "object") {
|
|
26
|
-
throw new Error("JWT payload must be a JSON object.");
|
|
27
|
-
}
|
|
28
|
-
if (typeof payloadJson.exp !== "number") {
|
|
29
|
-
throw new Error("JWT payload.exp must be a number.");
|
|
30
|
-
}
|
|
31
|
-
return payloadJson;
|
|
32
|
-
}
|
|
33
|
-
(0, test_1.test)("API: authenticate succeeds with valid credentials", async () => {
|
|
34
|
-
const api = await test_1.request.newContext({
|
|
35
|
-
baseURL,
|
|
36
|
-
extraHTTPHeaders: {
|
|
37
|
-
accept: "*/*",
|
|
38
|
-
"content-type": "application/json",
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
try {
|
|
42
|
-
const res = await api.post("/api/iam/authenticate", {
|
|
43
|
-
data: {
|
|
44
|
-
email: TEST_USER_EMAIL,
|
|
45
|
-
username: "",
|
|
46
|
-
password: TEST_USER_PASSWORD,
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
(0, test_1.expect)(res.status()).toBe(200);
|
|
50
|
-
const json = await res.json();
|
|
51
|
-
(0, test_1.expect)(json.statusCode).toBe(200);
|
|
52
|
-
(0, test_1.expect)(Array.isArray(json.message)).toBe(true);
|
|
53
|
-
(0, test_1.expect)(json.message.length).toBe(0);
|
|
54
|
-
(0, test_1.expect)(json.error).toBe("");
|
|
55
|
-
const user = json.data?.user;
|
|
56
|
-
(0, test_1.expect)(user, "Expected data.user to be an object.").toBeTruthy();
|
|
57
|
-
(0, test_1.expect)(typeof user).toBe("object");
|
|
58
|
-
const email = user?.email;
|
|
59
|
-
(0, test_1.expect)(typeof email).toBe("string");
|
|
60
|
-
if (email === TEST_USER_EMAIL) {
|
|
61
|
-
(0, test_1.expect)(email).toBe(TEST_USER_EMAIL);
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
(0, test_1.expect)(email.length).toBeGreaterThan(0);
|
|
65
|
-
}
|
|
66
|
-
(0, test_1.expect)(typeof user?.mobile).toBe("string");
|
|
67
|
-
(0, test_1.expect)(typeof user?.username).toBe("string");
|
|
68
|
-
(0, test_1.expect)(typeof user?.forcePasswordChange).toBe("boolean");
|
|
69
|
-
(0, test_1.expect)(typeof user?.id).toBe("number");
|
|
70
|
-
const roles = user?.roles;
|
|
71
|
-
(0, test_1.expect)(Array.isArray(roles)).toBe(true);
|
|
72
|
-
if (Array.isArray(roles)) {
|
|
73
|
-
(0, test_1.expect)(roles.every((role) => typeof role === "string")).toBe(true);
|
|
74
|
-
(0, test_1.expect)(roles).toContain("Admin");
|
|
75
|
-
}
|
|
76
|
-
const accessToken = json.data?.accessToken;
|
|
77
|
-
const refreshToken = json.data?.refreshToken;
|
|
78
|
-
(0, test_1.expect)(typeof accessToken).toBe("string");
|
|
79
|
-
(0, test_1.expect)(typeof refreshToken).toBe("string");
|
|
80
|
-
const accessPayload = validateJwt(accessToken);
|
|
81
|
-
const refreshPayload = validateJwt(refreshToken);
|
|
82
|
-
(0, test_1.expect)(typeof accessPayload.exp).toBe("number");
|
|
83
|
-
(0, test_1.expect)(typeof refreshPayload.exp).toBe("number");
|
|
84
|
-
}
|
|
85
|
-
finally {
|
|
86
|
-
await api.dispose();
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
(0, test_1.test)("API: authenticate fails with wrong password", async () => {
|
|
90
|
-
const api = await test_1.request.newContext({
|
|
91
|
-
baseURL,
|
|
92
|
-
extraHTTPHeaders: {
|
|
93
|
-
accept: "*/*",
|
|
94
|
-
"content-type": "application/json",
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
try {
|
|
98
|
-
const res = await api.post("/api/iam/authenticate", {
|
|
99
|
-
data: {
|
|
100
|
-
email: TEST_USER_EMAIL,
|
|
101
|
-
username: "",
|
|
102
|
-
password: `${TEST_USER_PASSWORD}__wrong`,
|
|
103
|
-
},
|
|
104
|
-
});
|
|
105
|
-
(0, test_1.expect)(res.status()).toBe(401);
|
|
106
|
-
const json = await res.json();
|
|
107
|
-
(0, test_1.expect)(json.statusCode).toBe(401);
|
|
108
|
-
(0, test_1.expect)(json.statusCodeMessage).toBe("Unauthorized");
|
|
109
|
-
(0, test_1.expect)(json.message).toBe("Invalid credentials");
|
|
110
|
-
(0, test_1.expect)(json.error).toBe("Invalid credentials");
|
|
111
|
-
(0, test_1.expect)(json.data?.statusCode).toBe(401);
|
|
112
|
-
(0, test_1.expect)(json.data?.error).toBe("Unauthorized");
|
|
113
|
-
(0, test_1.expect)(json.data?.message).toBe("Invalid credentials");
|
|
114
|
-
}
|
|
115
|
-
finally {
|
|
116
|
-
await api.dispose();
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
//# sourceMappingURL=authenticate.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"authenticate.spec.js","sourceRoot":"","sources":["../../tests/api/authenticate.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, request, test } from \"@playwright/test\";\nimport { getRequiredEnv } from \"../helpers/env\";\n\ntype JwtPayload = {\n exp?: number;\n [key: string]: unknown;\n};\n\nconst baseURL = process.env.API_BASE_URL ?? \"http://localhost:3000\";\nconst TEST_USER_EMAIL = getRequiredEnv(\"TEST_USER_EMAIL\");\nconst TEST_USER_PASSWORD = getRequiredEnv(\"TEST_USER_PASSWORD\");\n\nfunction base64UrlDecode(input: string): string {\n const normalized = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded =\n normalized.length % 4 === 0\n ? normalized\n : normalized.padEnd(\n normalized.length + (4 - (normalized.length % 4)),\n \"=\"\n );\n return Buffer.from(padded, \"base64\").toString(\"utf-8\");\n}\n\nfunction validateJwt(token: string): JwtPayload {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"JWT must have three dot-separated parts.\");\n }\n\n const headerJson = JSON.parse(base64UrlDecode(parts[0]));\n const payloadJson = JSON.parse(base64UrlDecode(parts[1]));\n\n if (!headerJson || typeof headerJson !== \"object\") {\n throw new Error(\"JWT header must be a JSON object.\");\n }\n\n if (!payloadJson || typeof payloadJson !== \"object\") {\n throw new Error(\"JWT payload must be a JSON object.\");\n }\n\n if (typeof payloadJson.exp !== \"number\") {\n throw new Error(\"JWT payload.exp must be a number.\");\n }\n\n return payloadJson as JwtPayload;\n}\n\ntest(\"API: authenticate succeeds with valid credentials\", async () => {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: {\n accept: \"*/*\",\n \"content-type\": \"application/json\",\n },\n });\n\n try {\n const res = await api.post(\"/api/iam/authenticate\", {\n data: {\n email: TEST_USER_EMAIL,\n username: \"\",\n password: TEST_USER_PASSWORD,\n },\n });\n\n expect(res.status()).toBe(200);\n const json = await res.json();\n\n expect(json.statusCode).toBe(200);\n expect(Array.isArray(json.message)).toBe(true);\n expect(json.message.length).toBe(0);\n expect(json.error).toBe(\"\");\n\n const user = json.data?.user as Record<string, unknown> | undefined;\n expect(user, \"Expected data.user to be an object.\").toBeTruthy();\n expect(typeof user).toBe(\"object\");\n\n const email = user?.email;\n expect(typeof email).toBe(\"string\");\n if (email === TEST_USER_EMAIL) {\n expect(email).toBe(TEST_USER_EMAIL);\n } else {\n // If your API returns a fixed system email, replace this with an exact match.\n expect((email as string).length).toBeGreaterThan(0);\n }\n\n expect(typeof user?.mobile).toBe(\"string\");\n expect(typeof user?.username).toBe(\"string\");\n expect(typeof user?.forcePasswordChange).toBe(\"boolean\");\n expect(typeof user?.id).toBe(\"number\");\n\n const roles = user?.roles;\n expect(Array.isArray(roles)).toBe(true);\n if (Array.isArray(roles)) {\n expect(roles.every((role) => typeof role === \"string\")).toBe(true);\n expect(roles).toContain(\"Admin\");\n }\n\n const accessToken = json.data?.accessToken;\n const refreshToken = json.data?.refreshToken;\n expect(typeof accessToken).toBe(\"string\");\n expect(typeof refreshToken).toBe(\"string\");\n\n const accessPayload = validateJwt(accessToken as string);\n const refreshPayload = validateJwt(refreshToken as string);\n\n expect(typeof accessPayload.exp).toBe(\"number\");\n expect(typeof refreshPayload.exp).toBe(\"number\");\n } finally {\n await api.dispose();\n }\n});\n\ntest(\"API: authenticate fails with wrong password\", async () => {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: {\n accept: \"*/*\",\n \"content-type\": \"application/json\",\n },\n });\n\n try {\n const res = await api.post(\"/api/iam/authenticate\", {\n data: {\n email: TEST_USER_EMAIL,\n username: \"\",\n password: `${TEST_USER_PASSWORD}__wrong`,\n },\n });\n\n expect(res.status()).toBe(401);\n const json = await res.json();\n\n expect(json.statusCode).toBe(401);\n expect(json.statusCodeMessage).toBe(\"Unauthorized\");\n expect(json.message).toBe(\"Invalid credentials\");\n expect(json.error).toBe(\"Invalid credentials\");\n expect(json.data?.statusCode).toBe(401);\n expect(json.data?.error).toBe(\"Unauthorized\");\n expect(json.data?.message).toBe(\"Invalid credentials\");\n } finally {\n await api.dispose();\n }\n});\n"]}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const test_1 = require("@playwright/test");
|
|
4
|
-
const auth_1 = require("../helpers/auth");
|
|
5
|
-
const baseURL = process.env.API_BASE_URL ?? "http://localhost:3000";
|
|
6
|
-
async function getCityByName(name) {
|
|
7
|
-
const api = await test_1.request.newContext({
|
|
8
|
-
baseURL,
|
|
9
|
-
extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
|
|
10
|
-
});
|
|
11
|
-
try {
|
|
12
|
-
const res = await api.get(`/api/city-master?filters[name][$eq]=${encodeURIComponent(name)}&limit=1&offset=0`);
|
|
13
|
-
(0, test_1.expect)(res.status()).toBe(200);
|
|
14
|
-
const json = await res.json();
|
|
15
|
-
const record = json?.data?.records?.[0];
|
|
16
|
-
(0, test_1.expect)(record, `Expected city '${name}' in testData`).toBeTruthy();
|
|
17
|
-
(0, test_1.expect)(record.id, "Expected record to have id").toBeTruthy();
|
|
18
|
-
return record;
|
|
19
|
-
}
|
|
20
|
-
finally {
|
|
21
|
-
await api.dispose();
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
test_1.test.describe("CRUDService.findOne (cityMaster)", () => {
|
|
25
|
-
(0, test_1.test)("returns a city by id", async () => {
|
|
26
|
-
const city = await getCityByName("Mumbai");
|
|
27
|
-
const api = await test_1.request.newContext({
|
|
28
|
-
baseURL,
|
|
29
|
-
extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
|
|
30
|
-
});
|
|
31
|
-
try {
|
|
32
|
-
const res = await api.get(`/api/city-master/${city.id}`);
|
|
33
|
-
(0, test_1.expect)(res.status()).toBe(200);
|
|
34
|
-
const json = await res.json();
|
|
35
|
-
(0, test_1.expect)(json?.data?.id).toBe(city.id);
|
|
36
|
-
(0, test_1.expect)(json?.data?.name).toBe("Mumbai");
|
|
37
|
-
(0, test_1.expect)(json?.data?.description).toBe("Mumbai city");
|
|
38
|
-
}
|
|
39
|
-
finally {
|
|
40
|
-
await api.dispose();
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
(0, test_1.test)("supports fields[] selection", async () => {
|
|
44
|
-
const city = await getCityByName("Pune");
|
|
45
|
-
const api = await test_1.request.newContext({
|
|
46
|
-
baseURL,
|
|
47
|
-
extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
|
|
48
|
-
});
|
|
49
|
-
try {
|
|
50
|
-
const res = await api.get(`/api/city-master/${city.id}?fields[]=id&fields[]=name`);
|
|
51
|
-
(0, test_1.expect)(res.status()).toBe(200);
|
|
52
|
-
const json = await res.json();
|
|
53
|
-
(0, test_1.expect)(json?.data?.id).toBe(city.id);
|
|
54
|
-
(0, test_1.expect)(json?.data?.name).toBe("Pune");
|
|
55
|
-
(0, test_1.expect)(json?.data?.description).toBeUndefined();
|
|
56
|
-
(0, test_1.expect)(json?.data?.state).toBeUndefined();
|
|
57
|
-
}
|
|
58
|
-
finally {
|
|
59
|
-
await api.dispose();
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
(0, test_1.test)("supports populate=state", async () => {
|
|
63
|
-
const city = await getCityByName("Bengaluru");
|
|
64
|
-
const api = await test_1.request.newContext({
|
|
65
|
-
baseURL,
|
|
66
|
-
extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
|
|
67
|
-
});
|
|
68
|
-
try {
|
|
69
|
-
const res = await api.get(`/api/city-master/${city.id}?populate=state`);
|
|
70
|
-
(0, test_1.expect)(res.status()).toBe(200);
|
|
71
|
-
const json = await res.json();
|
|
72
|
-
(0, test_1.expect)(json?.data?.name).toBe("Bengaluru");
|
|
73
|
-
(0, test_1.expect)(json?.data?.state).toBeTruthy();
|
|
74
|
-
(0, test_1.expect)(json?.data?.state?.name).toBe("Karnataka");
|
|
75
|
-
}
|
|
76
|
-
finally {
|
|
77
|
-
await api.dispose();
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
(0, test_1.test)("returns 404 for missing id", async () => {
|
|
81
|
-
const api = await test_1.request.newContext({
|
|
82
|
-
baseURL,
|
|
83
|
-
extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
|
|
84
|
-
});
|
|
85
|
-
try {
|
|
86
|
-
const res = await api.get(`/api/city-master/99999999`);
|
|
87
|
-
(0, test_1.expect)(res.status()).toBe(404);
|
|
88
|
-
const json = await res.json();
|
|
89
|
-
(0, test_1.expect)(String(json?.message)).toContain("cityMaster");
|
|
90
|
-
(0, test_1.expect)(String(json?.message)).toContain("not found");
|
|
91
|
-
}
|
|
92
|
-
finally {
|
|
93
|
-
await api.dispose();
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
//# sourceMappingURL=crud-service.findOne.cityMaster.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"crud-service.findOne.cityMaster.spec.js","sourceRoot":"","sources":["../../tests/api/crud-service.findOne.cityMaster.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, request, test } from \"@playwright/test\";\nimport { getAuthHeaders } from \"../helpers/auth\";\n\nconst baseURL = process.env.API_BASE_URL ?? \"http://localhost:3000\";\n\nasync function getCityByName(name: string) {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(\n `/api/city-master?filters[name][$eq]=${encodeURIComponent(name)}&limit=1&offset=0`\n );\n expect(res.status()).toBe(200);\n const json = await res.json();\n\n const record = json?.data?.records?.[0];\n expect(record, `Expected city '${name}' in testData`).toBeTruthy();\n expect(record.id, \"Expected record to have id\").toBeTruthy();\n\n return record;\n } finally {\n await api.dispose();\n }\n}\n\ntest.describe(\"CRUDService.findOne (cityMaster)\", () => {\n test(\"returns a city by id\", async () => {\n const city = await getCityByName(\"Mumbai\");\n\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(`/api/city-master/${city.id}`);\n expect(res.status()).toBe(200);\n\n const json = await res.json();\n expect(json?.data?.id).toBe(city.id);\n expect(json?.data?.name).toBe(\"Mumbai\");\n expect(json?.data?.description).toBe(\"Mumbai city\");\n } finally {\n await api.dispose();\n }\n });\n\n test(\"supports fields[] selection\", async () => {\n const city = await getCityByName(\"Pune\");\n\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(\n `/api/city-master/${city.id}?fields[]=id&fields[]=name`\n );\n expect(res.status()).toBe(200);\n\n const json = await res.json();\n expect(json?.data?.id).toBe(city.id);\n expect(json?.data?.name).toBe(\"Pune\");\n expect(json?.data?.description).toBeUndefined();\n expect(json?.data?.state).toBeUndefined();\n } finally {\n await api.dispose();\n }\n });\n\n test(\"supports populate=state\", async () => {\n const city = await getCityByName(\"Bengaluru\");\n\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(`/api/city-master/${city.id}?populate=state`);\n expect(res.status()).toBe(200);\n\n const json = await res.json();\n expect(json?.data?.name).toBe(\"Bengaluru\");\n expect(json?.data?.state).toBeTruthy();\n expect(json?.data?.state?.name).toBe(\"Karnataka\");\n } finally {\n await api.dispose();\n }\n });\n\n test(\"returns 404 for missing id\", async () => {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(`/api/city-master/99999999`);\n expect(res.status()).toBe(404);\n\n const json = await res.json();\n expect(String(json?.message)).toContain(\"cityMaster\");\n expect(String(json?.message)).toContain(\"not found\");\n } finally {\n await api.dispose();\n }\n });\n});\n"]}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const test_1 = require("@playwright/test");
|
|
4
|
-
(0, test_1.test)("GET /api/ping returns pong", async () => {
|
|
5
|
-
const baseURL = process.env.BASE_URL || "http://localhost:3000";
|
|
6
|
-
if (!baseURL) {
|
|
7
|
-
throw new Error("baseURL is not configured. Set API_BASE_URL or use the default.");
|
|
8
|
-
}
|
|
9
|
-
const api = await test_1.request.newContext({ baseURL });
|
|
10
|
-
const res = await api.get("/api/ping");
|
|
11
|
-
(0, test_1.expect)(res.status()).toBe(200);
|
|
12
|
-
const body = await res.json();
|
|
13
|
-
(0, test_1.expect)(body).toEqual({
|
|
14
|
-
statusCode: 200,
|
|
15
|
-
message: [],
|
|
16
|
-
error: "",
|
|
17
|
-
data: { pong: "v1.0.2" },
|
|
18
|
-
});
|
|
19
|
-
await api.dispose();
|
|
20
|
-
});
|
|
21
|
-
//# sourceMappingURL=ping.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ping.spec.js","sourceRoot":"","sources":["../../tests/api/ping.spec.ts"],"names":[],"mappings":";;AAAA,2CAAyD;AAEzD,IAAA,WAAI,EAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;IAC5C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,uBAAuB,CAAC;IAChE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;IACrF,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,cAAO,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAElD,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAEvC,IAAA,aAAM,EAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE/B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAA,aAAM,EAAC,IAAI,CAAC,CAAC,OAAO,CAAC;QACnB,UAAU,EAAE,GAAG;QACf,OAAO,EAAE,EAAE;QACX,KAAK,EAAE,EAAE;QACT,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KACzB,CAAC,CAAC;IAEH,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;AACtB,CAAC,CAAC,CAAC","sourcesContent":["import { expect, request, test } from \"@playwright/test\";\n\ntest(\"GET /api/ping returns pong\", async () => {\n const baseURL = process.env.BASE_URL || \"http://localhost:3000\";\n if (!baseURL) {\n throw new Error(\"baseURL is not configured. Set API_BASE_URL or use the default.\");\n }\n const api = await request.newContext({ baseURL });\n\n const res = await api.get(\"/api/ping\");\n\n expect(res.status()).toBe(200);\n\n const body = await res.json();\n expect(body).toEqual({\n statusCode: 200,\n message: [],\n error: \"\",\n data: { pong: \"v1.0.2\" },\n });\n\n await api.dispose();\n});\n"]}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getAccessToken = getAccessToken;
|
|
4
|
-
exports.getAuthHeaders = getAuthHeaders;
|
|
5
|
-
const test_1 = require("@playwright/test");
|
|
6
|
-
const env_1 = require("./env");
|
|
7
|
-
async function getAccessToken(baseURL) {
|
|
8
|
-
const TEST_USER_EMAIL = (0, env_1.getRequiredEnv)("TEST_USER_EMAIL");
|
|
9
|
-
const TEST_USER_PASSWORD = (0, env_1.getRequiredEnv)("TEST_USER_PASSWORD");
|
|
10
|
-
const api = await test_1.request.newContext({
|
|
11
|
-
baseURL,
|
|
12
|
-
extraHTTPHeaders: {
|
|
13
|
-
accept: "*/*",
|
|
14
|
-
"content-type": "application/json",
|
|
15
|
-
},
|
|
16
|
-
});
|
|
17
|
-
try {
|
|
18
|
-
const res = await api.post("/api/iam/authenticate", {
|
|
19
|
-
data: {
|
|
20
|
-
email: TEST_USER_EMAIL,
|
|
21
|
-
username: "",
|
|
22
|
-
password: TEST_USER_PASSWORD,
|
|
23
|
-
},
|
|
24
|
-
});
|
|
25
|
-
(0, test_1.expect)(res.status()).toBe(200);
|
|
26
|
-
const json = await res.json();
|
|
27
|
-
const token = json?.data?.accessToken;
|
|
28
|
-
(0, test_1.expect)(token, "Expected access token from authenticate endpoint.").toBeTruthy();
|
|
29
|
-
return token;
|
|
30
|
-
}
|
|
31
|
-
finally {
|
|
32
|
-
await api.dispose();
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
async function getAuthHeaders(baseURL) {
|
|
36
|
-
const token = await getAccessToken(baseURL);
|
|
37
|
-
return {
|
|
38
|
-
authorization: `Bearer ${token}`,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
//# sourceMappingURL=auth.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../tests/helpers/auth.ts"],"names":[],"mappings":";;AAGA,wCA6BC;AAED,wCAKC;AAvCD,2CAAmD;AACnD,+BAAuC;AAEhC,KAAK,UAAU,cAAc,CAAC,OAAe;IAClD,MAAM,eAAe,GAAG,IAAA,oBAAc,EAAC,iBAAiB,CAAC,CAAC;IAC1D,MAAM,kBAAkB,GAAG,IAAA,oBAAc,EAAC,oBAAoB,CAAC,CAAC;IAEhE,MAAM,GAAG,GAAG,MAAM,cAAO,CAAC,UAAU,CAAC;QACnC,OAAO;QACP,gBAAgB,EAAE;YAChB,MAAM,EAAE,KAAK;YACb,cAAc,EAAE,kBAAkB;SACnC;KACF,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE;YAClD,IAAI,EAAE;gBACJ,KAAK,EAAE,eAAe;gBACtB,QAAQ,EAAE,EAAE;gBACZ,QAAQ,EAAE,kBAAkB;aAC7B;SACF,CAAC,CAAC;QAEH,IAAA,aAAM,EAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,IAAI,EAAE,IAAI,EAAE,WAAiC,CAAC;QAC5D,IAAA,aAAM,EAAC,KAAK,EAAE,mDAAmD,CAAC,CAAC,UAAU,EAAE,CAAC;QAChF,OAAO,KAAe,CAAC;IACzB,CAAC;YAAS,CAAC;QACT,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;IACtB,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,cAAc,CAAC,OAAe;IAClD,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC;IAC5C,OAAO;QACL,aAAa,EAAE,UAAU,KAAK,EAAE;KACjC,CAAC;AACJ,CAAC","sourcesContent":["import { expect, request } from \"@playwright/test\";\nimport { getRequiredEnv } from \"./env\";\n\nexport async function getAccessToken(baseURL: string) {\n const TEST_USER_EMAIL = getRequiredEnv(\"TEST_USER_EMAIL\");\n const TEST_USER_PASSWORD = getRequiredEnv(\"TEST_USER_PASSWORD\");\n\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: {\n accept: \"*/*\",\n \"content-type\": \"application/json\",\n },\n });\n\n try {\n const res = await api.post(\"/api/iam/authenticate\", {\n data: {\n email: TEST_USER_EMAIL,\n username: \"\",\n password: TEST_USER_PASSWORD,\n },\n });\n\n expect(res.status()).toBe(200);\n const json = await res.json();\n const token = json?.data?.accessToken as string | undefined;\n expect(token, \"Expected access token from authenticate endpoint.\").toBeTruthy();\n return token as string;\n } finally {\n await api.dispose();\n }\n}\n\nexport async function getAuthHeaders(baseURL: string) {\n const token = await getAccessToken(baseURL);\n return {\n authorization: `Bearer ${token}`,\n };\n}\n"]}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getRequiredEnv = getRequiredEnv;
|
|
4
|
-
function getRequiredEnv(name) {
|
|
5
|
-
const value = process.env[name];
|
|
6
|
-
if (!value) {
|
|
7
|
-
throw new Error(`Missing required env var: ${name}`);
|
|
8
|
-
}
|
|
9
|
-
return value;
|
|
10
|
-
}
|
|
11
|
-
//# sourceMappingURL=env.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../tests/helpers/env.ts"],"names":[],"mappings":";;AAAA,wCAMC;AAND,SAAgB,cAAc,CAAC,IAAY;IACzC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,6BAA6B,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["export function getRequiredEnv(name: string): string {\n const value = process.env[name];\n if (!value) {\n throw new Error(`Missing required env var: ${name}`);\n }\n return value;\n}\n"]}
|