@tomei/sso 0.41.1 → 0.42.2
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/.commitlintrc.json +22 -22
- package/.eslintrc +16 -16
- package/.eslintrc.js +35 -35
- package/.gitlab-ci.yml +16 -16
- package/.husky/commit-msg +15 -15
- package/.husky/pre-commit +7 -7
- package/.prettierrc +4 -4
- package/Jenkinsfile +57 -57
- package/README.md +23 -23
- package/__tests__/unit/components/group/group.spec.ts +79 -79
- package/__tests__/unit/components/group-object-privilege/group-object-privilege.spec.ts +88 -88
- package/__tests__/unit/components/group-privilege/group-privilege.spec.ts +68 -68
- package/__tests__/unit/components/group-reporting-user/group-reporting-user.spec.ts +66 -66
- package/__tests__/unit/components/group-system-access/group-system-access.spec.ts +83 -83
- package/__tests__/unit/components/login-user/l.spec.ts +746 -746
- package/__tests__/unit/components/login-user/login.spec.ts +1164 -1164
- package/__tests__/unit/components/password-hash/password-hash.service.spec.ts +31 -31
- package/__tests__/unit/components/system/system.spec.ts +254 -254
- package/__tests__/unit/components/system-privilege/system-privilege.spec.ts +83 -83
- package/__tests__/unit/components/user-group/user-group.spec.ts +86 -86
- package/__tests__/unit/components/user-object-privilege/user-object-privilege.spec.ts +78 -78
- package/__tests__/unit/components/user-privilege/user-privilege.spec.ts +72 -72
- package/__tests__/unit/components/user-system-access/user-system-access.spec.ts +89 -89
- package/__tests__/unit/redis-client/redis.service.spec.ts +23 -23
- package/__tests__/unit/session/session.service.spec.ts +47 -47
- package/__tests__/unit/system-privilege/system-privilage.spec.ts +91 -91
- package/coverage/clover.xml +1452 -1452
- package/coverage/coverage-final.json +47 -47
- package/coverage/lcov-report/base.css +224 -224
- package/coverage/lcov-report/block-navigation.js +87 -87
- package/coverage/lcov-report/components/group/group.repository.ts.html +117 -117
- package/coverage/lcov-report/components/group/group.ts.html +327 -327
- package/coverage/lcov-report/components/group/index.html +130 -130
- package/coverage/lcov-report/components/group-object-privilege/group-object-privilege.repository.ts.html +117 -117
- package/coverage/lcov-report/components/group-object-privilege/group-object-privilege.ts.html +321 -321
- package/coverage/lcov-report/components/group-object-privilege/index.html +130 -130
- package/coverage/lcov-report/components/group-privilege/group-privilege.repository.ts.html +117 -117
- package/coverage/lcov-report/components/group-privilege/group-privilege.ts.html +303 -303
- package/coverage/lcov-report/components/group-privilege/index.html +130 -130
- package/coverage/lcov-report/components/group-reporting-user/group-reporting-user.repository.ts.html +117 -117
- package/coverage/lcov-report/components/group-reporting-user/group-reporting-user.ts.html +327 -327
- package/coverage/lcov-report/components/group-reporting-user/index.html +130 -130
- package/coverage/lcov-report/components/group-system-access/group-system-access.repository.ts.html +117 -117
- package/coverage/lcov-report/components/group-system-access/group-system-access.ts.html +309 -309
- package/coverage/lcov-report/components/group-system-access/index.html +130 -130
- package/coverage/lcov-report/components/login-history/index.html +115 -115
- package/coverage/lcov-report/components/login-history/login-history.repository.ts.html +117 -117
- package/coverage/lcov-report/components/login-user/index.html +130 -130
- package/coverage/lcov-report/components/login-user/login-user.ts.html +5007 -5007
- package/coverage/lcov-report/components/login-user/user.repository.ts.html +117 -117
- package/coverage/lcov-report/components/password-hash/index.html +115 -115
- package/coverage/lcov-report/components/password-hash/password-hash.service.ts.html +126 -126
- package/coverage/lcov-report/components/system/index.html +130 -130
- package/coverage/lcov-report/components/system/system.repository.ts.html +117 -117
- package/coverage/lcov-report/components/system/system.ts.html +909 -909
- package/coverage/lcov-report/components/system-privilege/index.html +130 -130
- package/coverage/lcov-report/components/system-privilege/system-privilege.repository.ts.html +120 -120
- package/coverage/lcov-report/components/system-privilege/system-privilege.ts.html +390 -390
- package/coverage/lcov-report/components/user-group/index.html +130 -130
- package/coverage/lcov-report/components/user-group/user-group.repository.ts.html +117 -117
- package/coverage/lcov-report/components/user-group/user-group.ts.html +354 -354
- package/coverage/lcov-report/components/user-object-privilege/index.html +130 -130
- package/coverage/lcov-report/components/user-object-privilege/user-object-privilege.repository.ts.html +117 -117
- package/coverage/lcov-report/components/user-object-privilege/user-object-privilege.ts.html +312 -312
- package/coverage/lcov-report/components/user-privilege/index.html +130 -130
- package/coverage/lcov-report/components/user-privilege/user-privilege.repository.ts.html +117 -117
- package/coverage/lcov-report/components/user-privilege/user-privilege.ts.html +306 -306
- package/coverage/lcov-report/components/user-system-access/index.html +130 -130
- package/coverage/lcov-report/components/user-system-access/user-system-access.repository.ts.html +117 -117
- package/coverage/lcov-report/components/user-system-access/user-system-access.ts.html +312 -312
- package/coverage/lcov-report/enum/group-type.enum.ts.html +108 -108
- package/coverage/lcov-report/enum/index.html +160 -160
- package/coverage/lcov-report/enum/index.ts.html +93 -93
- package/coverage/lcov-report/enum/user-status.enum.ts.html +105 -105
- package/coverage/lcov-report/enum/yn.enum.ts.html +96 -96
- package/coverage/lcov-report/index.html +370 -370
- package/coverage/lcov-report/models/group-object-privilege.entity.ts.html +333 -333
- package/coverage/lcov-report/models/group-privilege.entity.ts.html +315 -315
- package/coverage/lcov-report/models/group-reporting-user.entity.ts.html +339 -339
- package/coverage/lcov-report/models/group-system-access.entity.ts.html +324 -324
- package/coverage/lcov-report/models/group.entity.ts.html +435 -435
- package/coverage/lcov-report/models/index.html +310 -310
- package/coverage/lcov-report/models/login-history.entity.ts.html +252 -252
- package/coverage/lcov-report/models/staff.entity.ts.html +411 -411
- package/coverage/lcov-report/models/system-privilege.entity.ts.html +354 -354
- package/coverage/lcov-report/models/system.entity.ts.html +423 -423
- package/coverage/lcov-report/models/user-group.entity.ts.html +354 -354
- package/coverage/lcov-report/models/user-object-privilege.entity.ts.html +330 -330
- package/coverage/lcov-report/models/user-privilege.entity.ts.html +315 -315
- package/coverage/lcov-report/models/user-system-access.entity.ts.html +315 -315
- package/coverage/lcov-report/models/user.entity.ts.html +522 -522
- package/coverage/lcov-report/prettify.css +1 -1
- package/coverage/lcov-report/prettify.js +2 -2
- package/coverage/lcov-report/redis-client/index.html +115 -115
- package/coverage/lcov-report/redis-client/redis.service.ts.html +240 -240
- package/coverage/lcov-report/session/index.html +115 -115
- package/coverage/lcov-report/session/session.service.ts.html +246 -246
- package/coverage/lcov-report/sorter.js +196 -196
- package/coverage/lcov.info +2490 -2490
- package/coverage/test-report.xml +128 -128
- package/create-sso-user.sql +39 -39
- package/dist/__tests__/unit/components/group-privilege/group-privilege.test.d.ts +1 -0
- package/dist/__tests__/unit/components/group-privilege/group-privilege.test.js +71 -0
- package/dist/__tests__/unit/components/group-privilege/group-privilege.test.js.map +1 -0
- package/dist/__tests__/unit/components/login-user/login-user.spec.d.ts +0 -0
- package/dist/__tests__/unit/components/login-user/login-user.spec.js +6 -0
- package/dist/__tests__/unit/components/login-user/login-user.spec.js.map +1 -0
- package/dist/src/components/group-reporting-user/group-reporting-user.d.ts +2 -0
- package/dist/src/components/group-reporting-user/group-reporting-user.js +58 -0
- package/dist/src/components/group-reporting-user/group-reporting-user.js.map +1 -1
- package/dist/src/components/index.d.ts +1 -0
- package/dist/src/components/index.js +1 -0
- package/dist/src/components/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/jest.config.js +14 -14
- package/migrations/20240314080602-create-user-table.js +124 -124
- package/migrations/20240314080603-create-user-group-table.js +85 -85
- package/migrations/20240314080604-create-user-user-group-table.js +55 -55
- package/migrations/20240314080605-create-login-history-table.js +53 -53
- package/migrations/20240527064925-create-system-table.js +78 -78
- package/migrations/20240527064926-create-system-privilege-table.js +71 -71
- package/migrations/20240527065342-create-group-table.js +93 -93
- package/migrations/20240527065633-create-group-reporting-user-table.js +76 -76
- package/migrations/20240528011551-create-group-system-access-table.js +72 -72
- package/migrations/20240528023018-user-system-access-table.js +75 -75
- package/migrations/20240528032229-user-privilege-table.js +75 -75
- package/migrations/20240528063003-create-group-privilege-table.js +85 -85
- package/migrations/20240528063051-create-group-object-privilege-table.js +84 -84
- package/migrations/20240528063107-create-user-object-privilege-table.js +83 -83
- package/migrations/20240528063108-create-api-key-table.js +78 -78
- package/package.json +89 -89
- package/sampledotenv +7 -7
- package/sonar-project.properties +22 -22
- package/src/components/group-reporting-user/group-reporting-user.ts +119 -1
- package/src/components/index.ts +1 -0
- package/tsconfig.build.json +5 -5
- package/tsconfig.json +22 -22
package/sampledotenv
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
DATABASE_URL=
|
|
2
|
-
SHADOW_DATABASE_URL=
|
|
3
|
-
REDIS_URL=
|
|
4
|
-
REDIS_PASSWORD=
|
|
5
|
-
SMTP_HOST=
|
|
6
|
-
SMTP_PORT=
|
|
7
|
-
EMAIL_SENDER=
|
|
1
|
+
DATABASE_URL=
|
|
2
|
+
SHADOW_DATABASE_URL=
|
|
3
|
+
REDIS_URL=
|
|
4
|
+
REDIS_PASSWORD=
|
|
5
|
+
SMTP_HOST=
|
|
6
|
+
SMTP_PORT=
|
|
7
|
+
EMAIL_SENDER=
|
|
8
8
|
EMAIL_PASSWORD=
|
package/sonar-project.properties
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
sonar.projectKey=all-tomei-projects_sso
|
|
2
|
-
sonar.organization=all-tomei-projects
|
|
3
|
-
sonar.exclusions=**/*.js,test-data,dist,coverage, node_modules, __tests__, **/*.spec.ts, __mocks__
|
|
4
|
-
sonar.scm.provider=git
|
|
5
|
-
|
|
6
|
-
sonar.sources=src
|
|
7
|
-
sonar.test=__tests__
|
|
8
|
-
sonar.test.inclusions=src/**/*.spec.ts
|
|
9
|
-
|
|
10
|
-
sonar.javascript.lcov.reportPaths=./coverage/lcov.info
|
|
11
|
-
sonar.testExecutionReportPaths=coverage/test-report.xml
|
|
12
|
-
sonar.sourceEnconding=UTF-8
|
|
13
|
-
|
|
14
|
-
# This is the name and version displayed in the SonarCloud UI.
|
|
15
|
-
#sonar.projectName=sso
|
|
16
|
-
#sonar.projectVersion=1.0
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
|
|
20
|
-
#sonar.sources=.
|
|
21
|
-
|
|
22
|
-
# Encoding of the source code. Default is default system encoding
|
|
1
|
+
sonar.projectKey=all-tomei-projects_sso
|
|
2
|
+
sonar.organization=all-tomei-projects
|
|
3
|
+
sonar.exclusions=**/*.js,test-data,dist,coverage, node_modules, __tests__, **/*.spec.ts, __mocks__
|
|
4
|
+
sonar.scm.provider=git
|
|
5
|
+
|
|
6
|
+
sonar.sources=src
|
|
7
|
+
sonar.test=__tests__
|
|
8
|
+
sonar.test.inclusions=src/**/*.spec.ts
|
|
9
|
+
|
|
10
|
+
sonar.javascript.lcov.reportPaths=./coverage/lcov.info
|
|
11
|
+
sonar.testExecutionReportPaths=coverage/test-report.xml
|
|
12
|
+
sonar.sourceEnconding=UTF-8
|
|
13
|
+
|
|
14
|
+
# This is the name and version displayed in the SonarCloud UI.
|
|
15
|
+
#sonar.projectName=sso
|
|
16
|
+
#sonar.projectVersion=1.0
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
|
|
20
|
+
#sonar.sources=.
|
|
21
|
+
|
|
22
|
+
# Encoding of the source code. Default is default system encoding
|
|
23
23
|
#sonar.sourceEncoding=UTF-8
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { ClassError,
|
|
1
|
+
import { ClassError, ObjectBase } from '@tomei/general';
|
|
2
2
|
import { GroupReportingUserRepository } from './group-reporting-user.repository';
|
|
3
3
|
import { IGroupReportingUserAttr } from '../../interfaces/group-reporting-user.interface';
|
|
4
|
+
import { User } from '../login-user/user';
|
|
5
|
+
import { Group } from '../group/group';
|
|
6
|
+
import { ApplicationConfig } from '@tomei/config';
|
|
7
|
+
import { ActionEnum, Activity } from '@tomei/activity-history';
|
|
4
8
|
|
|
5
9
|
export class GroupReportingUser extends ObjectBase {
|
|
6
10
|
ObjectId: string;
|
|
@@ -78,4 +82,118 @@ export class GroupReportingUser extends ObjectBase {
|
|
|
78
82
|
);
|
|
79
83
|
}
|
|
80
84
|
}
|
|
85
|
+
|
|
86
|
+
async createGroupReportingUser(
|
|
87
|
+
loginUser: User, //The user performing the operation(typically the logged -in user).
|
|
88
|
+
dbTransaction: any, //Database transaction object to ensure the operation is atomic.
|
|
89
|
+
groupCode: string, //The code of the group to which the user is being assigned.
|
|
90
|
+
userId: number, //The ID of the user to be added to the group.
|
|
91
|
+
rank: number, //The rank to be assigned to the user in the group.
|
|
92
|
+
status: 'Active' | 'Inactive', //The initial status of the user in the group.
|
|
93
|
+
): Promise<GroupReportingUser> {
|
|
94
|
+
// Returns a GroupReportingUser instance representing the newly created record.
|
|
95
|
+
try {
|
|
96
|
+
//Creates a new group reporting user entry in the sso_GroupReportingUser table.
|
|
97
|
+
|
|
98
|
+
// Validate Input Parameters
|
|
99
|
+
// Ensure groupCode exists in the sso_Group table by calling the Group.init() method.
|
|
100
|
+
const group = await Group.init(dbTransaction, groupCode);
|
|
101
|
+
// Ensure userId exists in the sso_User table by calling the User.init() method.
|
|
102
|
+
const user = await User.init(dbTransaction, userId);
|
|
103
|
+
// Privilege Checking
|
|
104
|
+
// Call the loginUser.checkPrivileges() method by passing:
|
|
105
|
+
// SystemCode: Retrieve from app config.
|
|
106
|
+
// PrivilegeCode: "GROUP_REPORTING_USER_CREATE".
|
|
107
|
+
|
|
108
|
+
const systemCode =
|
|
109
|
+
ApplicationConfig.getComponentConfigValue('system-code');
|
|
110
|
+
const isPrivileged = await loginUser.checkPrivileges(
|
|
111
|
+
systemCode,
|
|
112
|
+
'GROUP_REPORTING_USER_CREATE',
|
|
113
|
+
);
|
|
114
|
+
if (!isPrivileged) {
|
|
115
|
+
throw new ClassError(
|
|
116
|
+
'GroupReportingUser',
|
|
117
|
+
'GroupReportingUserErrMsg02',
|
|
118
|
+
'Insufficient privileges to add a user to the group',
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Check for Duplicate User in Group
|
|
123
|
+
// Query the sso_GroupReportingUser table to see if the userId already exists in the specified groupCode.
|
|
124
|
+
const groupReportingUser = await GroupReportingUser._Repo.findOne({
|
|
125
|
+
where: {
|
|
126
|
+
GroupCode: groupCode,
|
|
127
|
+
UserId: userId,
|
|
128
|
+
},
|
|
129
|
+
transaction: dbTransaction,
|
|
130
|
+
});
|
|
131
|
+
// If the user already exists in the group, throw an error indicating the user is already part of the group.
|
|
132
|
+
if (groupReportingUser) {
|
|
133
|
+
throw new ClassError(
|
|
134
|
+
'GroupReportingUser',
|
|
135
|
+
'GroupReportingUserErrMsg03',
|
|
136
|
+
'User already exists in the group',
|
|
137
|
+
'createGroupReportingUser',
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
// Create GroupReportingUser Entry
|
|
141
|
+
// If validation and privilege checks pass, insert a new record in the sso_GroupReportingUser table with the provided groupCode, userId, rank, status, and loginUser.UserId.Automatically capture the current timestamp for CreatedAt.
|
|
142
|
+
this.GroupCode = groupCode;
|
|
143
|
+
this.UserId = userId;
|
|
144
|
+
this.Rank = rank;
|
|
145
|
+
this.Status = status;
|
|
146
|
+
this._CreatedById = loginUser.UserId;
|
|
147
|
+
this._CreatedAt = new Date();
|
|
148
|
+
this._UpdatedAt = new Date();
|
|
149
|
+
this._UpdatedById = loginUser.UserId;
|
|
150
|
+
|
|
151
|
+
const entityValueAfter: any = {
|
|
152
|
+
GroupCode: groupCode,
|
|
153
|
+
UserId: userId,
|
|
154
|
+
Rank: rank,
|
|
155
|
+
Status: status,
|
|
156
|
+
CreatedById: loginUser.UserId,
|
|
157
|
+
CreatedAt: this._CreatedAt,
|
|
158
|
+
UpdatedById: loginUser.UserId,
|
|
159
|
+
UpdatedAt: this._UpdatedAt,
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const newGroupReportingUser = await GroupReportingUser._Repo.create(
|
|
163
|
+
entityValueAfter,
|
|
164
|
+
{
|
|
165
|
+
transaction: dbTransaction,
|
|
166
|
+
},
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
entityValueAfter.GroupReportingUserId =
|
|
170
|
+
newGroupReportingUser.GroupReportingUserId;
|
|
171
|
+
|
|
172
|
+
// Record Create Activity
|
|
173
|
+
// Instantiate a new activity from the Activity class, and set:\
|
|
174
|
+
// ActivityId: activity.createId()
|
|
175
|
+
// Action: ActionEnum.Create
|
|
176
|
+
// Description: Create Group Reporting User
|
|
177
|
+
// EntityType: GroupReportingUser
|
|
178
|
+
// EntityId: newGroupReportingUser.GroupReportingUserId
|
|
179
|
+
// EntityValueBefore: Stringified empty object({})
|
|
180
|
+
// EntityValueAfter: EntityValueAfter(stringified representation of the newly created entity)
|
|
181
|
+
const activity = new Activity();
|
|
182
|
+
activity.Action = ActionEnum.CREATE;
|
|
183
|
+
activity.Description = 'Create Group Reporting User';
|
|
184
|
+
activity.EntityType = 'GroupReportingUser';
|
|
185
|
+
activity.EntityId = newGroupReportingUser.GroupReportingUserId.toString();
|
|
186
|
+
activity.EntityValueBefore = JSON.stringify({});
|
|
187
|
+
activity.EntityValueAfter = JSON.stringify(entityValueAfter);
|
|
188
|
+
// Call the activity create() method by passing:
|
|
189
|
+
// dbTransaction
|
|
190
|
+
// userId: loginUser.UserId
|
|
191
|
+
await activity.create(loginUser.ObjectId, dbTransaction);
|
|
192
|
+
// Return the Created GroupReportingUser
|
|
193
|
+
// Return the newly created GroupReportingUser instance, including all the relevant details like GroupReportingUserId, groupCode, userId, rank, status, and timestamps for CreatedAt.
|
|
194
|
+
return this;
|
|
195
|
+
} catch (error) {
|
|
196
|
+
throw error;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
81
199
|
}
|
package/src/components/index.ts
CHANGED
package/tsconfig.build.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "./tsconfig.json",
|
|
3
|
-
"include": ["**/*.ts"],
|
|
4
|
-
"exclude": ["node_modules", "__tests__", "dist", "**/*spec.ts"]
|
|
5
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"include": ["**/*.ts"],
|
|
4
|
+
"exclude": ["node_modules", "__tests__", "dist", "**/*spec.ts"]
|
|
5
|
+
}
|
|
6
6
|
|
package/tsconfig.json
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"module": "commonjs",
|
|
4
|
-
"declaration": true,
|
|
5
|
-
"removeComments": true,
|
|
6
|
-
"emitDecoratorMetadata": true,
|
|
7
|
-
"experimentalDecorators": true,
|
|
8
|
-
"allowSyntheticDefaultImports": true,
|
|
9
|
-
"moduleResolution": "node",
|
|
10
|
-
"target": "es6",
|
|
11
|
-
"sourceMap": true,
|
|
12
|
-
"outDir": "./dist",
|
|
13
|
-
"baseUrl": "./src",
|
|
14
|
-
"rootDir": "./",
|
|
15
|
-
"incremental": true,
|
|
16
|
-
"skipLibCheck": true,
|
|
17
|
-
"noImplicitAny": false,
|
|
18
|
-
"strictBindCallApply": false,
|
|
19
|
-
"forceConsistentCasingInFileNames": false,
|
|
20
|
-
"noFallthroughCasesInSwitch": false,
|
|
21
|
-
"strictNullChecks": false,
|
|
22
|
-
},
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "commonjs",
|
|
4
|
+
"declaration": true,
|
|
5
|
+
"removeComments": true,
|
|
6
|
+
"emitDecoratorMetadata": true,
|
|
7
|
+
"experimentalDecorators": true,
|
|
8
|
+
"allowSyntheticDefaultImports": true,
|
|
9
|
+
"moduleResolution": "node",
|
|
10
|
+
"target": "es6",
|
|
11
|
+
"sourceMap": true,
|
|
12
|
+
"outDir": "./dist",
|
|
13
|
+
"baseUrl": "./src",
|
|
14
|
+
"rootDir": "./",
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
"noImplicitAny": false,
|
|
18
|
+
"strictBindCallApply": false,
|
|
19
|
+
"forceConsistentCasingInFileNames": false,
|
|
20
|
+
"noFallthroughCasesInSwitch": false,
|
|
21
|
+
"strictNullChecks": false,
|
|
22
|
+
},
|
|
23
23
|
}
|