@solidxai/core 0.1.9-beta.8 → 0.1.10-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +89 -0
- package/README.md +3 -1
- package/dist/passport-strategies/facebook-oauth.strategy.d.ts +5 -3
- package/dist/passport-strategies/facebook-oauth.strategy.d.ts.map +1 -1
- package/dist/passport-strategies/facebook-oauth.strategy.js +41 -18
- package/dist/passport-strategies/facebook-oauth.strategy.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +1 -1
- package/dist/services/authentication.service.d.ts +12 -13
- package/dist/services/authentication.service.d.ts.map +1 -1
- package/dist/services/authentication.service.js +40 -16
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/settings/default-settings-provider.service.d.ts +16 -0
- package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
- package/dist/services/settings/default-settings-provider.service.js +75 -12
- package/dist/services/settings/default-settings-provider.service.js.map +1 -1
- package/dist/services/user.service.d.ts +10 -8
- package/dist/services/user.service.d.ts.map +1 -1
- package/dist/services/user.service.js +85 -46
- package/dist/services/user.service.js.map +1 -1
- package/package.json +2 -2
- package/src/passport-strategies/facebook-oauth.strategy.ts +82 -31
- package/src/seeders/seed-data/solid-core-metadata.json +1 -1
- package/src/services/authentication.service.ts +217 -141
- package/src/services/settings/default-settings-provider.service.ts +80 -17
- package/src/services/user.service.ts +149 -77
- package/dev-grooming-docs/ozzy-prompts.txt +0 -70
|
@@ -1,37 +1,68 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
BadRequestException,
|
|
3
|
+
forwardRef,
|
|
4
|
+
Inject,
|
|
5
|
+
Injectable,
|
|
6
|
+
} from "@nestjs/common";
|
|
2
7
|
import { ModuleRef } from "@nestjs/core";
|
|
3
|
-
import { InjectEntityManager, InjectRepository } from
|
|
4
|
-
import { CRUDService } from
|
|
5
|
-
import { EntityManager, Repository } from
|
|
8
|
+
import { InjectEntityManager, InjectRepository } from "@nestjs/typeorm";
|
|
9
|
+
import { CRUDService } from "src/services/crud.service";
|
|
10
|
+
import { EntityManager, Repository } from "typeorm";
|
|
6
11
|
import type { SolidCoreSetting } from "src/services/settings/default-settings-provider.service";
|
|
7
12
|
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { HashingService } from './hashing.service';
|
|
13
|
+
import { OauthUserDto } from "../dtos/oauth-user-dto";
|
|
14
|
+
import { RoleMetadata } from "../entities/role-metadata.entity";
|
|
15
|
+
import { User } from "../entities/user.entity";
|
|
16
|
+
import { ActiveUserData } from "../interfaces/active-user-data.interface";
|
|
17
|
+
import { ERROR_MESSAGES } from "src/constants/error-messages";
|
|
18
|
+
import { UserRepository } from "src/repository/user.repository";
|
|
19
|
+
import { RoleMetadataRepository } from "src/repository/role-metadata.repository";
|
|
20
|
+
import { HashingService } from "./hashing.service";
|
|
17
21
|
|
|
18
22
|
@Injectable()
|
|
19
23
|
export class UserService extends CRUDService<User> {
|
|
24
|
+
private buildFacebookUsernameBase(name?: string): string {
|
|
25
|
+
const normalized = (name || "")
|
|
26
|
+
.trim()
|
|
27
|
+
.toLowerCase()
|
|
28
|
+
.replace(/[^a-z0-9]+/g, "_")
|
|
29
|
+
.replace(/^_+|_+$/g, "");
|
|
30
|
+
return normalized || "facebook_user";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private async resolveUniqueUsername(
|
|
34
|
+
preferredUsername: string,
|
|
35
|
+
// fallbackUsername: string,
|
|
36
|
+
): Promise<string> {
|
|
37
|
+
let candidate = preferredUsername;
|
|
38
|
+
let suffix = 0;
|
|
39
|
+
|
|
40
|
+
while (await this.repo.findOne({ where: { username: candidate } })) {
|
|
41
|
+
suffix += 1;
|
|
42
|
+
candidate = `${preferredUsername}_${suffix}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (candidate) {
|
|
46
|
+
return candidate;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// return fallbackUsername;
|
|
50
|
+
}
|
|
51
|
+
|
|
20
52
|
constructor(
|
|
21
53
|
readonly hashingService: HashingService,
|
|
22
54
|
@InjectEntityManager()
|
|
23
55
|
readonly entityManager: EntityManager,
|
|
24
56
|
// @InjectRepository(User, 'default')
|
|
25
57
|
readonly repo: UserRepository,
|
|
26
|
-
@InjectRepository(User,
|
|
58
|
+
@InjectRepository(User, "default")
|
|
27
59
|
readonly nonSecurityRuleAwareRepo: Repository<User>,
|
|
28
60
|
// @InjectRepository(RoleMetadata)
|
|
29
61
|
// private readonly roleRepository: Repository<RoleMetadata>,
|
|
30
62
|
private readonly roleRepository: RoleMetadataRepository,
|
|
31
63
|
readonly moduleRef: ModuleRef,
|
|
32
|
-
|
|
33
64
|
) {
|
|
34
|
-
super(entityManager, repo,
|
|
65
|
+
super(entityManager, repo, "user", "solid-core", moduleRef);
|
|
35
66
|
}
|
|
36
67
|
|
|
37
68
|
override async delete(id: number, solidRequestContext: any = {}) {
|
|
@@ -44,13 +75,19 @@ export class UserService extends CRUDService<User> {
|
|
|
44
75
|
return super.delete(id, solidRequestContext);
|
|
45
76
|
}
|
|
46
77
|
|
|
47
|
-
override async deleteMany(
|
|
78
|
+
override async deleteMany(
|
|
79
|
+
ids: number[],
|
|
80
|
+
solidRequestContext: any = {},
|
|
81
|
+
): Promise<any> {
|
|
48
82
|
if (!ids || ids.length === 0) {
|
|
49
83
|
throw new Error(ERROR_MESSAGES.DELETE_IDS_REQUIRED);
|
|
50
84
|
}
|
|
51
85
|
|
|
52
86
|
// ❌ If the active user is trying to delete themselves
|
|
53
|
-
if (
|
|
87
|
+
if (
|
|
88
|
+
solidRequestContext?.activeUser?.sub &&
|
|
89
|
+
ids.includes(solidRequestContext.activeUser.id)
|
|
90
|
+
) {
|
|
54
91
|
throw new BadRequestException(ERROR_MESSAGES.DELETE_SELF_NOT_ALLOWED);
|
|
55
92
|
}
|
|
56
93
|
|
|
@@ -60,9 +97,9 @@ export class UserService extends CRUDService<User> {
|
|
|
60
97
|
async findOneByEmail(email: string): Promise<User> {
|
|
61
98
|
return await this.repo.findOne({
|
|
62
99
|
where: {
|
|
63
|
-
email: email
|
|
100
|
+
email: email,
|
|
64
101
|
},
|
|
65
|
-
relations: {}
|
|
102
|
+
relations: {},
|
|
66
103
|
});
|
|
67
104
|
// if (!entity) {
|
|
68
105
|
// throw new NotFoundException(`user with email #${email} not found`);
|
|
@@ -73,18 +110,18 @@ export class UserService extends CRUDService<User> {
|
|
|
73
110
|
async findOneByAccessCode(accessCode: string): Promise<User> {
|
|
74
111
|
return await this.repo.findOne({
|
|
75
112
|
where: {
|
|
76
|
-
accessCode: accessCode
|
|
113
|
+
accessCode: accessCode,
|
|
77
114
|
},
|
|
78
|
-
relations: {}
|
|
115
|
+
relations: {},
|
|
79
116
|
});
|
|
80
117
|
}
|
|
81
118
|
|
|
82
119
|
async findOneByUsername(username: string): Promise<User> {
|
|
83
120
|
return await this.repo.findOne({
|
|
84
121
|
where: {
|
|
85
|
-
username: username
|
|
122
|
+
username: username,
|
|
86
123
|
},
|
|
87
|
-
relations: {}
|
|
124
|
+
relations: {},
|
|
88
125
|
});
|
|
89
126
|
// if (!entity) {
|
|
90
127
|
// throw new NotFoundException(`user with username ${username} not found`);
|
|
@@ -96,8 +133,8 @@ export class UserService extends CRUDService<User> {
|
|
|
96
133
|
const user = await this.repo.findOne({
|
|
97
134
|
where: { id: id },
|
|
98
135
|
relations: {
|
|
99
|
-
roles: true
|
|
100
|
-
}
|
|
136
|
+
roles: true,
|
|
137
|
+
},
|
|
101
138
|
});
|
|
102
139
|
if (!user) {
|
|
103
140
|
throw new Error(ERROR_MESSAGES.USER_NOT_FOUND);
|
|
@@ -113,21 +150,22 @@ export class UserService extends CRUDService<User> {
|
|
|
113
150
|
const user = await this.repo.findOne({
|
|
114
151
|
where: { username: username },
|
|
115
152
|
relations: {
|
|
116
|
-
roles: true
|
|
117
|
-
}
|
|
153
|
+
roles: true,
|
|
154
|
+
},
|
|
118
155
|
});
|
|
119
156
|
if (!user) {
|
|
120
157
|
throw new Error(ERROR_MESSAGES.USER_NOT_FOUND_BY_USERNAME(username));
|
|
121
158
|
}
|
|
122
|
-
const role = await this.roleRepository.findOne({
|
|
159
|
+
const role = await this.roleRepository.findOne({
|
|
160
|
+
where: { name: roleName },
|
|
161
|
+
});
|
|
123
162
|
if (!role) {
|
|
124
163
|
throw new Error(ERROR_MESSAGES.ROLE_NOT_FOUND(roleName));
|
|
125
164
|
}
|
|
126
165
|
|
|
127
166
|
if (user.roles && user.roles.length > 0) {
|
|
128
167
|
user.roles.push(role);
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
168
|
+
} else {
|
|
131
169
|
user.roles = [role];
|
|
132
170
|
}
|
|
133
171
|
|
|
@@ -137,7 +175,7 @@ export class UserService extends CRUDService<User> {
|
|
|
137
175
|
async addRolesToUser(username: string, roleNames: string[]): Promise<User> {
|
|
138
176
|
const user = await this.nonSecurityRuleAwareRepo.findOne({
|
|
139
177
|
where: { username: username },
|
|
140
|
-
relations: { roles: true }
|
|
178
|
+
relations: { roles: true },
|
|
141
179
|
});
|
|
142
180
|
|
|
143
181
|
if (!user) {
|
|
@@ -145,43 +183,47 @@ export class UserService extends CRUDService<User> {
|
|
|
145
183
|
}
|
|
146
184
|
|
|
147
185
|
const roles = await this.roleRepository.find({
|
|
148
|
-
where: roleNames.map(roleName => ({ name: roleName }))
|
|
186
|
+
where: roleNames.map((roleName) => ({ name: roleName })),
|
|
149
187
|
});
|
|
150
188
|
|
|
151
189
|
if (roles.length !== roleNames.length) {
|
|
152
|
-
const foundRoleNames = roles.map(role => role.name);
|
|
153
|
-
const missingRoles = roleNames.filter(
|
|
190
|
+
const foundRoleNames = roles.map((role) => role.name);
|
|
191
|
+
const missingRoles = roleNames.filter(
|
|
192
|
+
(roleName) => !foundRoleNames.includes(roleName),
|
|
193
|
+
);
|
|
154
194
|
throw new Error(ERROR_MESSAGES.ROLES_NOT_FOUND(missingRoles));
|
|
155
195
|
}
|
|
156
196
|
|
|
157
|
-
const currentRoles = user.roles.map(role => role.name);
|
|
197
|
+
const currentRoles = user.roles.map((role) => role.name);
|
|
158
198
|
|
|
159
|
-
const rolesToAdd = roles.filter(
|
|
199
|
+
const rolesToAdd = roles.filter(
|
|
200
|
+
(role) => !currentRoles.includes(role.name),
|
|
201
|
+
);
|
|
160
202
|
|
|
161
|
-
const rolesToRemove = user.roles.filter(
|
|
203
|
+
const rolesToRemove = user.roles.filter(
|
|
204
|
+
(role) => !roleNames.includes(role.name),
|
|
205
|
+
);
|
|
162
206
|
|
|
163
207
|
if (rolesToAdd.length > 0) {
|
|
164
208
|
user.roles.push(...rolesToAdd);
|
|
165
209
|
}
|
|
166
210
|
|
|
167
211
|
if (rolesToRemove.length > 0) {
|
|
168
|
-
user.roles = user.roles.filter(role => !rolesToRemove.includes(role));
|
|
212
|
+
user.roles = user.roles.filter((role) => !rolesToRemove.includes(role));
|
|
169
213
|
}
|
|
170
214
|
|
|
171
215
|
return await this.nonSecurityRuleAwareRepo.save(user);
|
|
172
216
|
}
|
|
173
217
|
|
|
174
|
-
|
|
175
218
|
async removeRoleFromUser(username: string, roleName: string): Promise<User> {
|
|
176
|
-
|
|
177
219
|
// load the role with the respective permissions.
|
|
178
220
|
const user = await this.repo.findOne({
|
|
179
221
|
where: {
|
|
180
|
-
username: username
|
|
222
|
+
username: username,
|
|
181
223
|
},
|
|
182
224
|
relations: {
|
|
183
|
-
roles: true
|
|
184
|
-
}
|
|
225
|
+
roles: true,
|
|
226
|
+
},
|
|
185
227
|
});
|
|
186
228
|
|
|
187
229
|
if (!user) {
|
|
@@ -189,7 +231,7 @@ export class UserService extends CRUDService<User> {
|
|
|
189
231
|
}
|
|
190
232
|
|
|
191
233
|
// modify the permissions array.
|
|
192
|
-
user.roles = user.roles.filter(role => role.name !== roleName);
|
|
234
|
+
user.roles = user.roles.filter((role) => role.name !== roleName);
|
|
193
235
|
|
|
194
236
|
return await this.repo.save(user);
|
|
195
237
|
}
|
|
@@ -201,11 +243,11 @@ export class UserService extends CRUDService<User> {
|
|
|
201
243
|
email: oauthUserDto.email,
|
|
202
244
|
},
|
|
203
245
|
relations: {
|
|
204
|
-
roles: true
|
|
205
|
-
}
|
|
246
|
+
roles: true,
|
|
247
|
+
},
|
|
206
248
|
});
|
|
207
249
|
|
|
208
|
-
// if we are unable to find a user then we need to create one.
|
|
250
|
+
// if we are unable to find a user then we need to create one.
|
|
209
251
|
if (!user) {
|
|
210
252
|
const user = new User();
|
|
211
253
|
user.username = oauthUserDto.email;
|
|
@@ -220,9 +262,12 @@ export class UserService extends CRUDService<User> {
|
|
|
220
262
|
const savedUser = await this.repo.save(user);
|
|
221
263
|
|
|
222
264
|
// Initialize the user roles
|
|
223
|
-
await this.initializeRolesForNewUser(
|
|
265
|
+
await this.initializeRolesForNewUser(
|
|
266
|
+
[this.settingService.getConfigValue<SolidCoreSetting>("defaultRole")],
|
|
267
|
+
savedUser,
|
|
268
|
+
);
|
|
224
269
|
}
|
|
225
|
-
// else we update the user and store the generated code & access token.
|
|
270
|
+
// else we update the user and store the generated code & access token.
|
|
226
271
|
else {
|
|
227
272
|
const entity = await this.repo.preload({
|
|
228
273
|
id: user.id,
|
|
@@ -240,19 +285,48 @@ export class UserService extends CRUDService<User> {
|
|
|
240
285
|
}
|
|
241
286
|
|
|
242
287
|
async resolveUserOnOauthFacebook(oauthUserDto: OauthUserDto): Promise<User> {
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
288
|
+
const normalizedEmail = oauthUserDto.email?.trim().toLowerCase() || null;
|
|
289
|
+
let user: User | null = null;
|
|
290
|
+
|
|
291
|
+
if (oauthUserDto.providerId) {
|
|
292
|
+
user = await this.repo.findOne({
|
|
293
|
+
where: {
|
|
294
|
+
facebookId: oauthUserDto.providerId,
|
|
295
|
+
},
|
|
296
|
+
relations: {
|
|
297
|
+
roles: true,
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
}
|
|
251
301
|
|
|
252
302
|
if (!user) {
|
|
303
|
+
const facebookProviderFallback = `facebook_${oauthUserDto.providerId}`;
|
|
304
|
+
const facebookNameUsername = this.buildFacebookUsernameBase(
|
|
305
|
+
oauthUserDto.name,
|
|
306
|
+
);
|
|
307
|
+
// let username = normalizedEmail || facebookNameUsername;
|
|
308
|
+
let username = facebookNameUsername;
|
|
309
|
+
|
|
310
|
+
let email = normalizedEmail;
|
|
311
|
+
|
|
312
|
+
// Avoid clashing with local users that already own the same email/username.
|
|
313
|
+
if (normalizedEmail) {
|
|
314
|
+
const existingByEmail = await this.repo.findOne({
|
|
315
|
+
where: { email: normalizedEmail },
|
|
316
|
+
});
|
|
317
|
+
if (existingByEmail) {
|
|
318
|
+
username = facebookNameUsername;
|
|
319
|
+
email = null;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
username = await this.resolveUniqueUsername(
|
|
323
|
+
username,
|
|
324
|
+
// facebookProviderFallback,
|
|
325
|
+
);
|
|
326
|
+
|
|
253
327
|
const newUser = new User();
|
|
254
|
-
newUser.username =
|
|
255
|
-
newUser.email =
|
|
328
|
+
newUser.username = username;
|
|
329
|
+
newUser.email = email;
|
|
256
330
|
newUser.fullName = oauthUserDto.name;
|
|
257
331
|
newUser.lastLoginProvider = oauthUserDto.provider;
|
|
258
332
|
newUser.accessCode = oauthUserDto.accessCode;
|
|
@@ -263,7 +337,7 @@ export class UserService extends CRUDService<User> {
|
|
|
263
337
|
const savedUser = await this.repo.save(newUser);
|
|
264
338
|
|
|
265
339
|
await this.initializeRolesForNewUser(
|
|
266
|
-
[this.settingService.getConfigValue<SolidCoreSetting>(
|
|
340
|
+
[this.settingService.getConfigValue<SolidCoreSetting>("defaultRole")],
|
|
267
341
|
savedUser,
|
|
268
342
|
);
|
|
269
343
|
return savedUser;
|
|
@@ -276,13 +350,11 @@ export class UserService extends CRUDService<User> {
|
|
|
276
350
|
facebookId: oauthUserDto.providerId,
|
|
277
351
|
facebookProfilePicture: oauthUserDto.picture,
|
|
278
352
|
});
|
|
279
|
-
|
|
280
353
|
await this.repo.save(entity);
|
|
281
354
|
return entity;
|
|
282
355
|
}
|
|
283
356
|
}
|
|
284
357
|
|
|
285
|
-
|
|
286
358
|
async resolveUserOnOauthMicrosoft(oauthUserDto: OauthUserDto): Promise<User> {
|
|
287
359
|
const user = await this.repo.findOne({
|
|
288
360
|
where: {
|
|
@@ -307,10 +379,9 @@ export class UserService extends CRUDService<User> {
|
|
|
307
379
|
const savedUser = await this.repo.save(newUser);
|
|
308
380
|
|
|
309
381
|
await this.initializeRolesForNewUser(
|
|
310
|
-
[this.settingService.getConfigValue<SolidCoreSetting>(
|
|
382
|
+
[this.settingService.getConfigValue<SolidCoreSetting>("defaultRole")],
|
|
311
383
|
savedUser,
|
|
312
384
|
);
|
|
313
|
-
return savedUser;
|
|
314
385
|
} else {
|
|
315
386
|
const entity = await this.repo.preload({
|
|
316
387
|
id: user.id,
|
|
@@ -322,25 +393,29 @@ export class UserService extends CRUDService<User> {
|
|
|
322
393
|
});
|
|
323
394
|
|
|
324
395
|
await this.repo.save(entity);
|
|
325
|
-
return entity;
|
|
326
396
|
}
|
|
397
|
+
return user;
|
|
327
398
|
}
|
|
328
399
|
|
|
329
|
-
async findUsersByRole(
|
|
400
|
+
async findUsersByRole(
|
|
401
|
+
roleName: string,
|
|
402
|
+
relations: any = {},
|
|
403
|
+
): Promise<User[]> {
|
|
330
404
|
return await this.repo.find({
|
|
331
405
|
where: {
|
|
332
406
|
roles: {
|
|
333
|
-
name: roleName
|
|
334
|
-
}
|
|
407
|
+
name: roleName,
|
|
408
|
+
},
|
|
335
409
|
},
|
|
336
|
-
relations: relations
|
|
410
|
+
relations: relations,
|
|
337
411
|
});
|
|
338
412
|
}
|
|
339
413
|
|
|
340
414
|
async checkIfPermissionExists(query: any, activeUser: ActiveUserData) {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
415
|
+
const matchingPermssions = activeUser.permissions.filter((p) =>
|
|
416
|
+
query.permissionNames.includes(p),
|
|
417
|
+
);
|
|
418
|
+
return matchingPermssions;
|
|
344
419
|
}
|
|
345
420
|
|
|
346
421
|
async initializeRolesForNewUser(roles: string[], user: User) {
|
|
@@ -348,7 +423,7 @@ export class UserService extends CRUDService<User> {
|
|
|
348
423
|
throw new BadRequestException(ERROR_MESSAGES.USER_MISSING_ID);
|
|
349
424
|
}
|
|
350
425
|
let userRoles = [];
|
|
351
|
-
// Default Internal user role assigned
|
|
426
|
+
// Default Internal user role assigned
|
|
352
427
|
userRoles.push("Internal User");
|
|
353
428
|
if (roles) {
|
|
354
429
|
userRoles = [...userRoles, ...roles];
|
|
@@ -372,7 +447,4 @@ export class UserService extends CRUDService<User> {
|
|
|
372
447
|
passwordSchemeVersion: this.hashingService.currentVersion(),
|
|
373
448
|
};
|
|
374
449
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
450
|
}
|
|
378
|
-
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
Hi Ozzy,
|
|
2
|
-
Can you give me instructions to create a new model in the fees portal module, you can call it address-master.
|
|
3
|
-
This model needs to have the following fields.
|
|
4
|
-
1. address1 - shortText
|
|
5
|
-
2. address2 - shortText
|
|
6
|
-
3. addressType - selectionStatic with values - home, office & other.
|
|
7
|
-
4. city - many2one with the city model
|
|
8
|
-
5. state - many2one with the state model
|
|
9
|
-
6. pincode - many2one with the state model.
|
|
10
|
-
|
|
11
|
-
After I add this model I want to start storing student address.
|
|
12
|
-
|
|
13
|
-
To do this if you can also give me instructions on how to add a relation field to the student model, you can call this field studentAddress.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
Can you now create a new computed field provider.
|
|
21
|
-
- You can call this provider: StateTotalCitiesComputedFieldProvider
|
|
22
|
-
- This will be a post computation provider and will depend on the city model being inserted, deleted, updated.
|
|
23
|
-
- The implementation simply needs to take a count of cities that belong to the same state as the city being mutated and update the state totalCities field.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
Can you now create a new computed field provider.
|
|
27
|
-
- You can call this provider: StateTotalPopulationComputedFieldProvider
|
|
28
|
-
- This will be a post computation provider and will depend on the city model being inserted, deleted, updated.
|
|
29
|
-
- The implementation simply needs to take a sum of population of each city that belong to the same state as the city being mutated and update the state totalPopulation field.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
Can you now create a new computed field provider.
|
|
33
|
-
- You can call this provider: StateTotalGdpComputedFieldProvider
|
|
34
|
-
- This will be a post computation provider and will depend on the city model being inserted, deleted, updated.
|
|
35
|
-
- The implementation simply needs to take a sum of gdp of each city that belong to the same state as the city being mutated and update the state totalGdp field.
|
|
36
|
-
- I will add the totalGdp field to the state model after I have the computed provider TS code.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
# Testing prompt sequence
|
|
40
|
-
|
|
41
|
-
## Create Module
|
|
42
|
-
Can you create a new module called address-master. This will be used to store address master data.
|
|
43
|
-
|
|
44
|
-
## Add Model - Country
|
|
45
|
-
Can you add a new model to the address-master module called country.
|
|
46
|
-
This will have name, description (richText), countryCode. You can assume name & countryCode to be unique. But only name will be used as a userkey.
|
|
47
|
-
Also enable audit tracking on the model and all its fields.
|
|
48
|
-
Make sure the table name is properly prefixed with the module name snake case.
|
|
49
|
-
|
|
50
|
-
## Add Model - Language
|
|
51
|
-
Can you add a new model to the address-master module called language.
|
|
52
|
-
This will have language, languageIsoCode. You can assume language to be unique.
|
|
53
|
-
Also for the languageIsoCode, can you create a field with type staticSelection add a bunch (20 odd, include at-least 7-8 indian languages also) of commonly spoken languages with their respective language codes as values.
|
|
54
|
-
Also enable audit tracking on the model and all its fields.
|
|
55
|
-
Make sure the table name is properly prefixed with the module name snake case.
|
|
56
|
-
|
|
57
|
-
## Add Model - Country Language
|
|
58
|
-
|
|
59
|
-
Can you now add a new model called CountrySpokenLanguages, this model will have 2 many-to-one fields.
|
|
60
|
-
1. country - many-to-one with the country model.
|
|
61
|
-
2. spokenLanguage - many-to-one with the language model.
|
|
62
|
-
3. spokenBy - int field storing data representing how many people speak that language.
|
|
63
|
-
Also enable audit tracking on the model and all its fields.
|
|
64
|
-
Make sure the table name is properly prefixed with the module name snake case.
|
|
65
|
-
|
|
66
|
-
## Add Model - Country Population Trend
|
|
67
|
-
Can you add a model called CountryPopulationTrend, this model has the following fields.
|
|
68
|
-
1. country - many-to-one with the country model.
|
|
69
|
-
2. entryDate - date field.
|
|
70
|
-
3. populationAsOnDate - int field storing the population of that country as on that date.
|