@solidxai/core 0.1.6-beta.9 → 0.1.6

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.
Files changed (115) hide show
  1. package/.claude/settings.local.json +15 -0
  2. package/dist/controllers/scheduled-job.controller.d.ts +0 -1
  3. package/dist/controllers/scheduled-job.controller.d.ts.map +1 -1
  4. package/dist/controllers/scheduled-job.controller.js +0 -12
  5. package/dist/controllers/scheduled-job.controller.js.map +1 -1
  6. package/dist/entities/chatter-message-details.entity.d.ts +0 -1
  7. package/dist/entities/chatter-message-details.entity.d.ts.map +1 -1
  8. package/dist/entities/chatter-message-details.entity.js +1 -5
  9. package/dist/entities/chatter-message-details.entity.js.map +1 -1
  10. package/dist/entities/common.entity.js +1 -1
  11. package/dist/entities/common.entity.js.map +1 -1
  12. package/dist/entities/legacy-common.entity.d.ts.map +1 -1
  13. package/dist/entities/legacy-common.entity.js +1 -1
  14. package/dist/entities/legacy-common.entity.js.map +1 -1
  15. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts +1 -0
  16. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts.map +1 -1
  17. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js +8 -9
  18. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js.map +1 -1
  19. package/dist/interfaces.d.ts +1 -4
  20. package/dist/interfaces.d.ts.map +1 -1
  21. package/dist/interfaces.js.map +1 -1
  22. package/dist/seeders/seed-data/solid-core-metadata.json +3 -73
  23. package/dist/services/authentication.service.d.ts.map +1 -1
  24. package/dist/services/authentication.service.js +21 -45
  25. package/dist/services/authentication.service.js.map +1 -1
  26. package/dist/services/chatter-message.service.d.ts.map +1 -1
  27. package/dist/services/chatter-message.service.js +0 -26
  28. package/dist/services/chatter-message.service.js.map +1 -1
  29. package/dist/services/dashboard-question.service.d.ts +0 -4
  30. package/dist/services/dashboard-question.service.d.ts.map +1 -1
  31. package/dist/services/dashboard-question.service.js +8 -22
  32. package/dist/services/dashboard-question.service.js.map +1 -1
  33. package/dist/services/model-metadata.service.d.ts.map +1 -1
  34. package/dist/services/model-metadata.service.js +6 -101
  35. package/dist/services/model-metadata.service.js.map +1 -1
  36. package/dist/services/question-data-providers/chartjs-sql-data-provider.service.d.ts +4 -2
  37. package/dist/services/question-data-providers/chartjs-sql-data-provider.service.d.ts.map +1 -1
  38. package/dist/services/question-data-providers/chartjs-sql-data-provider.service.js +1 -2
  39. package/dist/services/question-data-providers/chartjs-sql-data-provider.service.js.map +1 -1
  40. package/dist/services/question-data-providers/prime-react-datatable-sql-data-provider.service.d.ts +5 -2
  41. package/dist/services/question-data-providers/prime-react-datatable-sql-data-provider.service.d.ts.map +1 -1
  42. package/dist/services/question-data-providers/prime-react-datatable-sql-data-provider.service.js +1 -2
  43. package/dist/services/question-data-providers/prime-react-datatable-sql-data-provider.service.js.map +1 -1
  44. package/dist/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.d.ts +5 -2
  45. package/dist/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.d.ts.map +1 -1
  46. package/dist/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.js +1 -2
  47. package/dist/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.js.map +1 -1
  48. package/dist/services/queues/database-subscriber.service.d.ts +2 -4
  49. package/dist/services/queues/database-subscriber.service.d.ts.map +1 -1
  50. package/dist/services/queues/database-subscriber.service.js +1 -9
  51. package/dist/services/queues/database-subscriber.service.js.map +1 -1
  52. package/dist/services/queues/publisher-factory.service.d.ts.map +1 -1
  53. package/dist/services/queues/publisher-factory.service.js +6 -4
  54. package/dist/services/queues/publisher-factory.service.js.map +1 -1
  55. package/dist/services/queues/rabbitmq-subscriber.service.d.ts +2 -4
  56. package/dist/services/queues/rabbitmq-subscriber.service.d.ts.map +1 -1
  57. package/dist/services/queues/rabbitmq-subscriber.service.js +1 -9
  58. package/dist/services/queues/rabbitmq-subscriber.service.js.map +1 -1
  59. package/dist/services/scheduled-job.service.d.ts +1 -6
  60. package/dist/services/scheduled-job.service.d.ts.map +1 -1
  61. package/dist/services/scheduled-job.service.js +2 -26
  62. package/dist/services/scheduled-job.service.js.map +1 -1
  63. package/dist/services/scheduled-jobs/scheduler.interface.d.ts +0 -2
  64. package/dist/services/scheduled-jobs/scheduler.interface.d.ts.map +1 -1
  65. package/dist/services/scheduled-jobs/scheduler.interface.js.map +1 -1
  66. package/dist/services/scheduled-jobs/scheduler.service.d.ts +2 -6
  67. package/dist/services/scheduled-jobs/scheduler.service.d.ts.map +1 -1
  68. package/dist/services/scheduled-jobs/scheduler.service.js +15 -55
  69. package/dist/services/scheduled-jobs/scheduler.service.js.map +1 -1
  70. package/dist/services/selection-providers/list-of-dashboard-question-providers-selection-provider.service.d.ts.map +1 -1
  71. package/dist/services/selection-providers/list-of-dashboard-question-providers-selection-provider.service.js +1 -4
  72. package/dist/services/selection-providers/list-of-dashboard-question-providers-selection-provider.service.js.map +1 -1
  73. package/dist/transformers/typeorm/local-date-time-transformer.d.ts +4 -4
  74. package/dist/transformers/typeorm/local-date-time-transformer.d.ts.map +1 -1
  75. package/dist/transformers/typeorm/local-date-time-transformer.js +28 -25
  76. package/dist/transformers/typeorm/local-date-time-transformer.js.map +1 -1
  77. package/package.json +1 -1
  78. package/src/controllers/scheduled-job.controller.ts +0 -6
  79. package/src/entities/chatter-message-details.entity.ts +0 -3
  80. package/src/entities/common.entity.ts +2 -2
  81. package/src/entities/legacy-common.entity.ts +4 -3
  82. package/src/helpers/field-crud-managers/MediaFieldCrudManager.ts +9 -9
  83. package/src/interfaces.ts +1 -7
  84. package/src/seeders/seed-data/solid-core-metadata.json +3 -73
  85. package/src/services/1.js +6 -0
  86. package/src/services/authentication.service.ts +24 -47
  87. package/src/services/chatter-message.service.ts +0 -26
  88. package/src/services/dashboard-question.service.ts +4 -23
  89. package/src/services/model-metadata.service.ts +7 -109
  90. package/src/services/question-data-providers/chartjs-sql-data-provider.service.ts +7 -3
  91. package/src/services/question-data-providers/prime-react-datatable-sql-data-provider.service.ts +8 -4
  92. package/src/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.ts +8 -4
  93. package/src/services/queues/database-subscriber.service.ts +1 -12
  94. package/src/services/queues/publisher-factory.service.ts +6 -8
  95. package/src/services/queues/rabbitmq-subscriber.service.ts +1 -12
  96. package/src/services/scheduled-job.service.ts +2 -31
  97. package/src/services/scheduled-jobs/scheduler.interface.ts +1 -4
  98. package/src/services/scheduled-jobs/scheduler.service.ts +16 -60
  99. package/src/services/selection-providers/list-of-dashboard-question-providers-selection-provider.service.ts +1 -4
  100. package/src/transformers/typeorm/local-date-time-transformer.ts +33 -41
  101. package/dist/services/question-data-providers/interfaces.d.ts +0 -1
  102. package/dist/services/question-data-providers/interfaces.d.ts.map +0 -1
  103. package/dist/services/question-data-providers/interfaces.js +0 -1
  104. package/dist/services/question-data-providers/interfaces.js.map +0 -1
  105. package/dist-tests/api/authenticate.spec.js +0 -119
  106. package/dist-tests/api/authenticate.spec.js.map +0 -1
  107. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +0 -97
  108. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +0 -1
  109. package/dist-tests/api/ping.spec.js +0 -21
  110. package/dist-tests/api/ping.spec.js.map +0 -1
  111. package/dist-tests/helpers/auth.js +0 -41
  112. package/dist-tests/helpers/auth.js.map +0 -1
  113. package/dist-tests/helpers/env.js +0 -11
  114. package/dist-tests/helpers/env.js.map +0 -1
  115. package/src/services/question-data-providers/interfaces.ts +0 -0
@@ -1,12 +1,10 @@
1
- import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common';
1
+ import { Injectable, Logger } from '@nestjs/common';
2
2
  import { ModuleRef } from "@nestjs/core";
3
3
  import { InjectEntityManager } from '@nestjs/typeorm';
4
4
  import { ScheduledJob } from 'src/entities/scheduled-job.entity';
5
- import { SolidRegistry } from 'src/helpers/solid-registry';
6
5
  import { ScheduledJobRepository } from 'src/repository/scheduled-job.repository';
7
6
  import { EntityManager } from 'typeorm';
8
7
  import { CRUDService } from './crud.service';
9
- import { SchedulerServiceImpl } from './scheduled-jobs/scheduler.service';
10
8
 
11
9
  @Injectable()
12
10
  export class ScheduledJobService extends CRUDService<ScheduledJob> {
@@ -18,36 +16,9 @@ export class ScheduledJobService extends CRUDService<ScheduledJob> {
18
16
  // @InjectRepository(ScheduledJob)
19
17
  // readonly repo: Repository<ScheduledJob>,
20
18
  readonly repo: ScheduledJobRepository,
21
- readonly moduleRef: ModuleRef,
22
- private readonly solidRegistry: SolidRegistry,
23
- private readonly schedulerService: SchedulerServiceImpl,
19
+ readonly moduleRef: ModuleRef
24
20
 
25
21
  ) {
26
22
  super(entityManager, repo, 'scheduledJob', 'solid-core', moduleRef);
27
23
  }
28
-
29
- async triggerRun(id: number): Promise<ScheduledJob> {
30
- const job = await this.repo.findOne({ where: { id } });
31
- if (!job) {
32
- throw new NotFoundException(`Scheduled job with id ${id} not found`);
33
- }
34
-
35
- const handler = this.solidRegistry.getScheduledJobProviderInstance(job.job);
36
- if (!handler) {
37
- throw new BadRequestException(`Scheduled job handler not found: ${job.job}`);
38
- }
39
-
40
- this.logger.log(`Manually triggering scheduled job id=${job.id}, job=${job.job}`);
41
-
42
- await handler.execute(job);
43
-
44
- const finishedAt = new Date();
45
- job.lastRunAt = finishedAt;
46
- job.nextRunAt = this.schedulerService.computeNextRunAt(job, finishedAt);
47
- await this.repo.save(job);
48
-
49
- this.logger.log(`Completed manual trigger for scheduled job id=${job.id}, nextRunAt=${job.nextRunAt?.toISOString?.()}`);
50
-
51
- return job;
52
- }
53
24
  }
@@ -1,6 +1,3 @@
1
- import { ScheduledJob } from "src/entities/scheduled-job.entity";
2
-
3
1
  export interface ISchedulerService {
4
2
  runScheduledJobs(): Promise<void>;
5
- computeNextRunAt(job: ScheduledJob, from: Date): Date;
6
- }
3
+ }
@@ -94,21 +94,22 @@ export class SchedulerServiceImpl implements ISchedulerService {
94
94
  }
95
95
 
96
96
  private shouldRunNow(job: ScheduledJob, now: Date): boolean {
97
- const today = new Date(now);
98
- today.setHours(0, 0, 0, 0);
99
- const timeNow = this.toHHMM(now); // hh:mm
97
+ const today = now.toISOString().split('T')[0]; // yyyy-mm-dd
98
+ const timeNow = now.toTimeString().slice(0, 5); // hh:mm
100
99
 
101
100
  // 1. Check startDate / endDate
102
- const startDate = this.toDateOnly(job.startDate as unknown as Date | string | null);
103
- const endDate = this.toDateOnly(job.endDate as unknown as Date | string | null);
104
- if (startDate && today < startDate) return false;
105
- if (endDate && today > endDate) return false;
101
+ if (job.startDate && new Date(today) < new Date(job.startDate)) return false;
102
+ if (job.endDate && new Date(today) > new Date(job.endDate)) return false;
106
103
 
107
104
  // 2. Check startTime / endTime
108
- const jobStart = this.toHHMM(job.startTime as unknown as Date | string | null);
109
- const jobEnd = this.toHHMM(job.endTime as unknown as Date | string | null);
110
- if (jobStart && timeNow < jobStart) return false;
111
- if (jobEnd && timeNow > jobEnd) return false;
105
+ if (job.startTime) {
106
+ const jobStart = job.startTime.toTimeString().slice(0, 5);
107
+ if (timeNow < jobStart) return false;
108
+ }
109
+ if (job.endTime) {
110
+ const jobEnd = job.endTime.toTimeString().slice(0, 5);
111
+ if (timeNow > jobEnd) return false;
112
+ }
112
113
 
113
114
  // 3. Check custom frequency
114
115
  if (job.frequency.toLowerCase() === 'custom') {
@@ -120,7 +121,8 @@ export class SchedulerServiceImpl implements ISchedulerService {
120
121
  // 3. Check dayOfWeek (for weekly)
121
122
  if (job.frequency.toLowerCase() === 'weekly' && job.dayOfWeek) {
122
123
  const todayName = now.toLocaleString('en-US', { weekday: 'long' }); // e.g., "Monday"
123
- const days = this.parseDayOfWeek(job.dayOfWeek);
124
+ // const days = job.dayOfWeek.split(',').map(d => d.trim());
125
+ const days = JSON.parse(job.dayOfWeek) as string[];
124
126
  if (!days.includes(todayName)) return false;
125
127
  }
126
128
 
@@ -133,53 +135,7 @@ export class SchedulerServiceImpl implements ISchedulerService {
133
135
  return true;
134
136
  }
135
137
 
136
- private parseDayOfWeek(dayOfWeek: string): string[] {
137
- try {
138
- const parsed = JSON.parse(dayOfWeek);
139
- return Array.isArray(parsed) ? parsed : [];
140
- } catch (error) {
141
- this.logger.warn(`Invalid dayOfWeek JSON '${dayOfWeek}'`, error as any);
142
- return [];
143
- }
144
- }
145
-
146
- private toDateOnly(value: Date | string | null | undefined): Date | null {
147
- if (!value) return null;
148
-
149
- if (value instanceof Date) {
150
- const d = new Date(value);
151
- d.setHours(0, 0, 0, 0);
152
- return d;
153
- }
154
-
155
- const parsed = new Date(value);
156
- if (Number.isNaN(parsed.getTime())) return null;
157
-
158
- parsed.setHours(0, 0, 0, 0);
159
- return parsed;
160
- }
161
-
162
- private toHHMM(value: Date | string | null | undefined): string | null {
163
- if (!value) return null;
164
-
165
- if (value instanceof Date) {
166
- return value.toTimeString().slice(0, 5);
167
- }
168
-
169
- if (typeof value === 'string') {
170
- const match = value.match(/^(\d{2}):(\d{2})/);
171
- if (match) return `${match[1]}:${match[2]}`;
172
-
173
- const parsed = new Date(value);
174
- if (!Number.isNaN(parsed.getTime())) {
175
- return parsed.toTimeString().slice(0, 5);
176
- }
177
- }
178
-
179
- return null;
180
- }
181
-
182
- public computeNextRunForCustomCron(job: ScheduledJob, from: Date): Date {
138
+ private computeNextRunForCustomCron(job: ScheduledJob, from: Date): Date {
183
139
  const base = new Date(from);
184
140
 
185
141
  if (!job.cronExpression) {
@@ -209,7 +165,7 @@ export class SchedulerServiceImpl implements ISchedulerService {
209
165
  }
210
166
  }
211
167
 
212
- public computeNextRunAt(job: ScheduledJob, from: Date): Date {
168
+ private computeNextRunAt(job: ScheduledJob, from: Date): Date {
213
169
  const base = new Date(from);
214
170
 
215
171
  switch (job.frequency.toLowerCase()) {
@@ -3,7 +3,6 @@ import { SelectionProvider } from "src/decorators/selection-provider.decorator";
3
3
  import { SolidRegistry } from "src/helpers/solid-registry";
4
4
  import { IDashboardQuestionDataProvider, IDashboardVariableSelectionProvider, ISelectionProvider, ISelectionProviderContext, ISelectionProviderValues } from "../../interfaces";
5
5
  import { SQL_DYNAMIC_PROVIDER_NAME } from "../dashboard.service";
6
- import { CHARTJS_SQL_DATA_PROVIDER_NAME, INBUILT_SQL_DATA_PROVIDERS, PRIME_REACT_DATATABLE_SQL_DATA_PROVIDER_NAME, PRIME_REACT_METER_GROUP_SQL_DATA_PROVIDER_NAME } from "../dashboard-question.service";
7
6
 
8
7
 
9
8
  @SelectionProvider()
@@ -35,9 +34,7 @@ export class ListOfDashboardQuestionProvidersSelectionProvider implements ISelec
35
34
  async values(query: string, ctxt: ISelectionProviderContext): Promise<readonly ISelectionProviderValues[]> {
36
35
  const dashboardSelectionProviders = this.solidRegistry.getDashboardQuestionDataProviders()
37
36
  //Exclude the SQL dynamic provider from the list, (since although it is a dashboard selection provider, it is not a valid option for the user to select)
38
- return dashboardSelectionProviders
39
- .filter(provider => !INBUILT_SQL_DATA_PROVIDERS.includes(provider.name()))
40
- .map(i => {
37
+ return dashboardSelectionProviders.map(i => {
41
38
  return { label: i.name, value: i.name };
42
39
  });
43
40
  }
@@ -20,67 +20,59 @@ function dateToUtcComponentString(d: Date): string {
20
20
  const ss = pad(d.getUTCSeconds());
21
21
  const ms = pad(d.getUTCMilliseconds(), 3);
22
22
 
23
- // A "naive" timestamp string representing the DB wall-clock components
23
+ // A naive timestamp string representing the DB wall-clock components
24
24
  return `${yyyy}-${mm}-${dd} ${hh}:${mi}:${ss}.${ms}`;
25
25
  }
26
26
 
27
- function getWallClockConfig(): { tz: string; wallTimeMode: boolean } {
28
- return {
29
- tz: process.env.SOLIDX_WALL_TIME_TIMEZONE || process.env.SOLIDX_TIMEZONE || "UTC",
30
- wallTimeMode: (process.env.SOLIDX_TIME_STORED_AS_WALL_TIME || "").toLowerCase() === "true",
31
- };
32
- }
33
-
34
- /**
35
- * Returns a dayjs instance positioned at the wall-clock time for the given Date.
36
- * - Wall-clock mode ON: dayjs in the configured timezone (components = wall-clock components)
37
- * - Wall-clock mode OFF: dayjs in UTC
38
- * Counterpart to serializeDate — use this to format/display a date value correctly.
39
- */
40
- export function parseDate(date: Date): dayjs.Dayjs {
41
- const { tz, wallTimeMode } = getWallClockConfig();
42
- if (!wallTimeMode) return dayjs.utc(date);
43
- return dayjs(date).tz(tz);
44
- }
27
+ export const LocalDateTimeTransformer: ValueTransformer & { utc: ValueTransformer } = {
28
+ // DB → Entity
29
+ from(value: Date | string | null | undefined): Date | null | undefined {
45
30
 
46
- /**
47
- * Converts a Date to a string for storage in plain text columns (e.g. audit values).
48
- * - Wall-clock mode ON: "YYYY-MM-DD HH:mm:ss.SSS" in the configured timezone (no Z suffix)
49
- * - Wall-clock mode OFF: ISO 8601 UTC string with Z suffix
50
- * The presence/absence of the Z suffix lets consumers distinguish the two cases.
51
- */
52
- export function serializeDate(date: Date): string {
53
- const { wallTimeMode } = getWallClockConfig();
54
- if (!wallTimeMode) return date.toISOString();
55
- return parseDate(date).format("YYYY-MM-DD HH:mm:ss.SSS");
56
- }
31
+ const SOLIDX_WALL_TIME_TZ = process.env.SOLIDX_WALL_TIME_TIMEZONE || process.env.SOLIDX_TIMEZONE || "UTC";
32
+ const SOLIDX_TIME_STORED_AS_WALL_TIME = (process.env.SOLIDX_TIME_STORED_AS_WALL_TIME || "").toLowerCase() === "true";
57
33
 
58
- export const LocalDateTimeTransformer: ValueTransformer = {
59
- // DB -> Entity
60
- from(value: Date | string | null | undefined): Date | null | undefined {
61
- // critical... super important to return undefined here
34
+ // critical... super important to return undefined here
62
35
  if (value === undefined) return undefined;
63
36
  if (value === null) return null;
64
37
 
65
- const { tz, wallTimeMode } = getWallClockConfig();
66
-
67
- if (!wallTimeMode) {
38
+ if (!SOLIDX_TIME_STORED_AS_WALL_TIME) {
68
39
  return dayjs(value).toDate();
69
40
  }
70
41
 
71
42
  const naive = value instanceof Date ? dateToUtcComponentString(value) : String(value);
72
- return dayjs.tz(naive, tz).utc().toDate();
43
+ return dayjs.tz(naive, SOLIDX_WALL_TIME_TZ).utc().toDate();
73
44
  },
74
45
 
75
- // Entity -> DB
46
+ // Entity DB
76
47
  to(value: Date | null | undefined): Date | null | undefined {
77
- // critical... super important to return undefined here
48
+
49
+ const SOLIDX_WALL_TIME_TZ = process.env.SOLIDX_WALL_TIME_TIMEZONE || process.env.SOLIDX_TIMEZONE || "UTC";
50
+ const SOLIDX_TIME_STORED_AS_WALL_TIME = (process.env.SOLIDX_TIME_STORED_AS_WALL_TIME || "").toLowerCase() === "true";
51
+
52
+ // critical... super important to return undefined here
78
53
  if (value === undefined) return undefined;
79
54
  if (value === null) return null;
80
55
 
81
- const wallTimeStr = serializeDate(value);
56
+ if (!SOLIDX_TIME_STORED_AS_WALL_TIME) {
57
+ return dayjs(value).toDate();
58
+ }
59
+
60
+ const wallTimeStr = dayjs(value).tz(SOLIDX_WALL_TIME_TZ).format("YYYY-MM-DD HH:mm:ss.SSS");
82
61
  return dayjs.utc(wallTimeStr).toDate();
83
62
  },
84
63
 
64
+ utc: {
65
+ from(value: Date | string | null | undefined): Date | null | undefined {
66
+ if (value === undefined) return undefined;
67
+ if (value === null) return null;
68
+ return dayjs(value).toDate();
69
+ },
70
+ to(value: Date | null | undefined): Date | null | undefined {
71
+ if (value === undefined) return undefined;
72
+ if (value === null) return null;
73
+ return dayjs(value).toDate();
74
+ }
75
+ }
85
76
  };
86
77
 
78
+ export const UtcDateTimeTransformer = LocalDateTimeTransformer.utc;
@@ -1 +0,0 @@
1
- //# sourceMappingURL=interfaces.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../../src/services/question-data-providers/interfaces.ts"],"names":[],"mappings":""}
@@ -1 +0,0 @@
1
- //# sourceMappingURL=interfaces.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../../../src/services/question-data-providers/interfaces.ts"],"names":[],"mappings":"","sourcesContent":[""]}
@@ -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"]}