@loomcore/api 0.2.45 → 0.2.47
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/dist/__tests__/common-test.utils.d.ts +2 -0
- package/dist/__tests__/common-test.utils.js +6 -0
- package/dist/__tests__/setup/vitest-setup.js +3 -2
- package/dist/databases/models/constants.js +6 -1
- package/dist/databases/mongo-db/utils/build-no-sql-match.util.js +28 -4
- package/dist/databases/mongo-db/utils/convert-strings-to-object-ids.util.js +6 -5
- package/dist/middleware/authorize/authenticate-request.js +7 -1
- package/dist/utils/create-public-spec.utils.js +1 -4
- package/package.json +2 -2
|
@@ -24,6 +24,7 @@ declare function setupTestUsers(): Promise<{
|
|
|
24
24
|
declare function deleteTestUser(): Promise<void>;
|
|
25
25
|
declare function simulateloginWithTestUser(): Promise<string>;
|
|
26
26
|
declare function getAuthToken(): string;
|
|
27
|
+
declare function getExpiredAuthToken(): string;
|
|
27
28
|
declare function verifyToken(token: string): any;
|
|
28
29
|
export declare class CategoryService extends GenericApiService<ICategory> {
|
|
29
30
|
constructor(database: IDatabase);
|
|
@@ -65,6 +66,7 @@ declare const testUtils: {
|
|
|
65
66
|
deleteMetaOrg: typeof deleteMetaOrg;
|
|
66
67
|
deleteTestUser: typeof deleteTestUser;
|
|
67
68
|
getAuthToken: typeof getAuthToken;
|
|
69
|
+
getExpiredAuthToken: typeof getExpiredAuthToken;
|
|
68
70
|
getTestMetaOrgRefererUrl: typeof testObjectsModule.getTestMetaOrgRefererUrl;
|
|
69
71
|
initialize: typeof initialize;
|
|
70
72
|
setupTestConfig: typeof setupTestConfig;
|
|
@@ -208,6 +208,11 @@ function getAuthToken() {
|
|
|
208
208
|
const token = jwt.sign(userContext, JWT_SECRET, { expiresIn: 3600 });
|
|
209
209
|
return `Bearer ${token}`;
|
|
210
210
|
}
|
|
211
|
+
function getExpiredAuthToken() {
|
|
212
|
+
const userContext = getTestMetaOrgUserContext();
|
|
213
|
+
const token = jwt.sign(userContext, JWT_SECRET, { expiresIn: -1 });
|
|
214
|
+
return `Bearer ${token}`;
|
|
215
|
+
}
|
|
211
216
|
function verifyToken(token) {
|
|
212
217
|
return jwt.verify(token, JWT_SECRET);
|
|
213
218
|
}
|
|
@@ -402,6 +407,7 @@ const testUtils = {
|
|
|
402
407
|
deleteMetaOrg,
|
|
403
408
|
deleteTestUser,
|
|
404
409
|
getAuthToken,
|
|
410
|
+
getExpiredAuthToken,
|
|
405
411
|
getTestMetaOrgRefererUrl,
|
|
406
412
|
initialize,
|
|
407
413
|
setupTestConfig,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { setIdSchema } from '@loomcore/common/validation';
|
|
1
|
+
import { initializeTypeBox, setIdSchema, TypeboxObjectId } from '@loomcore/common/validation';
|
|
2
2
|
import { Type } from '@sinclair/typebox';
|
|
3
|
+
initializeTypeBox();
|
|
3
4
|
if (!process.env.TEST_DATABASE) {
|
|
4
5
|
process.env.TEST_DATABASE = 'postgres';
|
|
5
6
|
}
|
|
@@ -8,7 +9,7 @@ if (testDatabase === 'postgres') {
|
|
|
8
9
|
setIdSchema(Type.Number({ title: 'ID', integer: true, minimum: 1 }));
|
|
9
10
|
}
|
|
10
11
|
else if (testDatabase === 'mongodb') {
|
|
11
|
-
setIdSchema(
|
|
12
|
+
setIdSchema(TypeboxObjectId({ title: 'ID' }));
|
|
12
13
|
}
|
|
13
14
|
else {
|
|
14
15
|
throw new Error(`Invalid TEST_DATABASE value: ${testDatabase}. Must be 'postgres' or 'mongodb'`);
|
|
@@ -2,17 +2,42 @@ import { entityUtils } from '@loomcore/common/utils';
|
|
|
2
2
|
import { ObjectId } from 'mongodb';
|
|
3
3
|
import { PROPERTIES_THAT_ARE_NOT_OBJECT_IDS } from '../../models/constants.js';
|
|
4
4
|
import { getPropertySchema } from '../../utils/get-property-schema.util.js';
|
|
5
|
+
function isExcludedFromObjectIdConversion(key) {
|
|
6
|
+
return PROPERTIES_THAT_ARE_NOT_OBJECT_IDS.includes(key);
|
|
7
|
+
}
|
|
8
|
+
function shouldConvertEqToObjectId(key, propSchema, hasSchema) {
|
|
9
|
+
if (isExcludedFromObjectIdConversion(key)) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
if (key === '_id' || propSchema?.format === 'objectid') {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
return !hasSchema && key.endsWith('Id');
|
|
16
|
+
}
|
|
17
|
+
function shouldConvertInToObjectIds(key, propSchema, hasSchema) {
|
|
18
|
+
if (isExcludedFromObjectIdConversion(key)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (key === '_id' || propSchema?.format === 'objectid') {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
const isObjectIdArray = propSchema?.type === 'array' && propSchema.items?.format === 'objectid';
|
|
25
|
+
if (isObjectIdArray) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return !hasSchema && (key.endsWith('Id') || key.endsWith('Ids'));
|
|
29
|
+
}
|
|
5
30
|
export function buildNoSqlMatch(queryOptions, modelSpec) {
|
|
6
31
|
const filters = queryOptions.filters || {};
|
|
7
32
|
const schema = modelSpec?.fullSchema;
|
|
33
|
+
const hasSchema = !!schema;
|
|
8
34
|
let match = {};
|
|
9
35
|
for (const [key, value] of Object.entries(filters)) {
|
|
10
36
|
if (value) {
|
|
11
37
|
const propSchema = schema ? getPropertySchema(key, schema) : undefined;
|
|
12
38
|
if (value.eq !== undefined) {
|
|
13
|
-
const isObjectIdField = propSchema?.format === 'objectid';
|
|
14
39
|
const valueToCompare = value.eq;
|
|
15
|
-
if ((key
|
|
40
|
+
if (shouldConvertEqToObjectId(key, propSchema, hasSchema)
|
|
16
41
|
&& typeof valueToCompare === 'string' && entityUtils.isValidObjectId(valueToCompare)) {
|
|
17
42
|
match[key] = new ObjectId(valueToCompare);
|
|
18
43
|
}
|
|
@@ -24,8 +49,7 @@ export function buildNoSqlMatch(queryOptions, modelSpec) {
|
|
|
24
49
|
}
|
|
25
50
|
}
|
|
26
51
|
if (value.in !== undefined && Array.isArray(value.in)) {
|
|
27
|
-
|
|
28
|
-
if (key === '_id' || isObjectIdArray || (!schema && (key.endsWith('Id') || key.endsWith('Ids')) && !PROPERTIES_THAT_ARE_NOT_OBJECT_IDS.includes(key))) {
|
|
52
|
+
if (shouldConvertInToObjectIds(key, propSchema, hasSchema)) {
|
|
29
53
|
const objectIds = value.in
|
|
30
54
|
.filter(val => typeof val === 'string' && entityUtils.isValidObjectId(val))
|
|
31
55
|
.map(val => new ObjectId(val));
|
|
@@ -26,10 +26,11 @@ export function convertStringsToObjectIds(entity, schema) {
|
|
|
26
26
|
const items = subSchema.items;
|
|
27
27
|
if (!items)
|
|
28
28
|
return obj;
|
|
29
|
+
const propertyName = path[path.length - 1];
|
|
30
|
+
if (propertyName && PROPERTIES_THAT_ARE_NOT_OBJECT_IDS.includes(propertyName)) {
|
|
31
|
+
return obj;
|
|
32
|
+
}
|
|
29
33
|
if (items.format === 'objectid') {
|
|
30
|
-
if (path.length === 1 && PROPERTIES_THAT_ARE_NOT_OBJECT_IDS.includes(path[0])) {
|
|
31
|
-
return obj;
|
|
32
|
-
}
|
|
33
34
|
return obj.map(item => {
|
|
34
35
|
if (typeof item === 'string' && entityUtils.isValidObjectId(item)) {
|
|
35
36
|
return new ObjectId(item);
|
|
@@ -51,10 +52,10 @@ export function convertStringsToObjectIds(entity, schema) {
|
|
|
51
52
|
const typedPropSchema = propSchema;
|
|
52
53
|
const fullPath = [...path, key];
|
|
53
54
|
const value = result[key];
|
|
54
|
-
|
|
55
|
-
if (isObjectIdField && path.length === 0 && PROPERTIES_THAT_ARE_NOT_OBJECT_IDS.includes(key)) {
|
|
55
|
+
if (PROPERTIES_THAT_ARE_NOT_OBJECT_IDS.includes(key)) {
|
|
56
56
|
continue;
|
|
57
57
|
}
|
|
58
|
+
const isObjectIdField = typedPropSchema.format === 'objectid';
|
|
58
59
|
if (isObjectIdField && typeof value === 'string' && entityUtils.isValidObjectId(value)) {
|
|
59
60
|
result[key] = new ObjectId(value);
|
|
60
61
|
}
|
|
@@ -16,6 +16,12 @@ export function authenticateRequest(req) {
|
|
|
16
16
|
throw new UnauthenticatedError();
|
|
17
17
|
}
|
|
18
18
|
const authConfig = getAuthConfig();
|
|
19
|
-
|
|
19
|
+
let rawPayload;
|
|
20
|
+
try {
|
|
21
|
+
rawPayload = jwt.verify(token, authConfig.clientSecret);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
throw new UnauthenticatedError();
|
|
25
|
+
}
|
|
20
26
|
return getAuthUserContextSpec().decode(rawPayload);
|
|
21
27
|
}
|
|
@@ -4,10 +4,7 @@ export function createPublicSpec(spec) {
|
|
|
4
4
|
if (config.debug?.showAuditFields) {
|
|
5
5
|
return spec;
|
|
6
6
|
}
|
|
7
|
-
|
|
8
|
-
isAuditable: spec.isAuditable,
|
|
7
|
+
return entityUtils.getModelSpec(spec.schema, {
|
|
9
8
|
isEntity: spec.isEntity,
|
|
10
|
-
addAuditableSchema: false,
|
|
11
9
|
});
|
|
12
|
-
return publicSpec;
|
|
13
10
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loomcore/api",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.47",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Loom Core Api - An opinionated Node.js api using Typescript, Express, and MongoDb or PostgreSQL",
|
|
6
6
|
"scripts": {
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"reflect-metadata": "^0.2.2"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
|
-
"@loomcore/common": "^0.0.
|
|
63
|
+
"@loomcore/common": "^0.0.86",
|
|
64
64
|
"@sinclair/typebox": "0.34.33",
|
|
65
65
|
"cookie-parser": "^1.4.6",
|
|
66
66
|
"cors": "^2.8.5",
|