Compare commits
33 Commits
AUT-657
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
![]() |
06b99ac58a | ||
![]() |
f0e194e584 | ||
![]() |
d4b9331cf2 | ||
![]() |
4c49367910 | ||
![]() |
a506c4411d | ||
![]() |
1859c9854e | ||
![]() |
6ff29b9ae6 | ||
![]() |
3578f6b849 | ||
![]() |
0347864fde | ||
![]() |
5f9786a2c7 | ||
![]() |
75aeff1898 | ||
![]() |
0afcdce6d3 | ||
![]() |
a591d0ea87 | ||
![]() |
0e111a3532 | ||
![]() |
b599466ffa | ||
![]() |
69727e78df | ||
![]() |
02ae67b147 | ||
![]() |
a769f78801 | ||
![]() |
d583e42428 | ||
![]() |
da732becb6 | ||
![]() |
b89a4d58d9 | ||
![]() |
09854147d1 | ||
![]() |
3648c2bfe3 | ||
![]() |
3f3ee032f6 | ||
![]() |
68e5d54331 | ||
![]() |
824c434b0b | ||
![]() |
9f0e0ca656 | ||
![]() |
95f89ba03e | ||
![]() |
697f72ecf4 | ||
![]() |
4f03f2ab51 | ||
![]() |
c81531cb7a | ||
![]() |
7b6e4aa153 | ||
![]() |
f21039d19d |
@@ -33,7 +33,6 @@
|
||||
"axios": "1.6.0",
|
||||
"bcrypt": "^5.0.1",
|
||||
"bullmq": "^3.0.0",
|
||||
"copyfiles": "^2.4.1",
|
||||
"cors": "^2.8.5",
|
||||
"crypto-js": "^4.1.1",
|
||||
"debug": "~2.6.9",
|
||||
@@ -45,7 +44,6 @@
|
||||
"graphql-middleware": "^6.1.15",
|
||||
"graphql-shield": "^7.5.0",
|
||||
"graphql-tools": "^8.2.0",
|
||||
"graphql-type-json": "^0.3.2",
|
||||
"handlebars": "^4.7.7",
|
||||
"http-errors": "~1.6.3",
|
||||
"http-proxy-agent": "^7.0.0",
|
||||
@@ -59,7 +57,7 @@
|
||||
"morgan": "^1.10.0",
|
||||
"multer": "1.4.5-lts.1",
|
||||
"node-html-markdown": "^1.3.0",
|
||||
"nodemailer": "6.7.0",
|
||||
"nodemailer": "6.9.9",
|
||||
"oauth-1.0a": "^2.2.6",
|
||||
"objection": "^3.0.0",
|
||||
"passport": "^0.6.0",
|
||||
@@ -68,7 +66,6 @@
|
||||
"pluralize": "^8.0.0",
|
||||
"raw-body": "^2.5.2",
|
||||
"showdown": "^2.1.0",
|
||||
"stripe": "^11.13.0",
|
||||
"winston": "^3.7.1",
|
||||
"xmlrpc": "^1.3.2"
|
||||
},
|
||||
|
@@ -0,0 +1,6 @@
|
||||
import appConfig from '../../../../config/app.js';
|
||||
import { renderObject } from '../../../../helpers/renderer.js';
|
||||
|
||||
export default async (request, response) => {
|
||||
renderObject(response, { version: appConfig.version });
|
||||
};
|
@@ -0,0 +1,26 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import request from 'supertest';
|
||||
import app from '../../../../app.js';
|
||||
|
||||
describe('GET /api/v1/automatisch/version', () => {
|
||||
it('should return Automatisch version', async () => {
|
||||
const response = await request(app)
|
||||
.get('/api/v1/automatisch/version')
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = {
|
||||
data: {
|
||||
version: '0.10.0',
|
||||
},
|
||||
meta: {
|
||||
count: 1,
|
||||
currentPage: null,
|
||||
isArray: false,
|
||||
totalPages: null,
|
||||
type: 'Object',
|
||||
},
|
||||
};
|
||||
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
@@ -0,0 +1,5 @@
|
||||
import { renderObject } from '../../../../helpers/renderer.js';
|
||||
|
||||
export default async (request, response) => {
|
||||
renderObject(response, request.currentUser);
|
||||
};
|
@@ -0,0 +1,26 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import request from 'supertest';
|
||||
import app from '../../../../app.js';
|
||||
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
|
||||
import { createUser } from '../../../../../test/factories/user';
|
||||
import getCurrentUserMock from '../../../../../test/mocks/rest/api/v1/users/get-current-user';
|
||||
|
||||
describe('GET /api/v1/users/me', () => {
|
||||
let role, currentUser, token;
|
||||
|
||||
beforeEach(async () => {
|
||||
currentUser = await createUser();
|
||||
role = await currentUser.$relatedQuery('role');
|
||||
token = createAuthTokenByUserId(currentUser.id);
|
||||
});
|
||||
|
||||
it('should return current user info', async () => {
|
||||
const response = await request(app)
|
||||
.get('/api/v1/users/me')
|
||||
.set('Authorization', token)
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = getCurrentUserMock(currentUser, role);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
@@ -0,0 +1,12 @@
|
||||
import { renderObject } from '../../../../helpers/renderer.js';
|
||||
|
||||
export default async (request, response) => {
|
||||
const inTrial = await request.currentUser.inTrial();
|
||||
|
||||
const trialInfo = {
|
||||
inTrial,
|
||||
expireAt: request.currentUser.trialExpiryDate,
|
||||
};
|
||||
|
||||
renderObject(response, trialInfo);
|
||||
};
|
@@ -0,0 +1,38 @@
|
||||
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||
import request from 'supertest';
|
||||
import app from '../../../../app.js';
|
||||
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id.js';
|
||||
import { createUser } from '../../../../../test/factories/user.js';
|
||||
import getUserTrialMock from '../../../../../test/mocks/rest/api/v1/users/get-user-trial.js';
|
||||
import appConfig from '../../../../config/app.js';
|
||||
import { DateTime } from 'luxon';
|
||||
import User from '../../../../models/user.js';
|
||||
|
||||
describe('GET /api/v1/users/:userId/trial', () => {
|
||||
let user, token;
|
||||
|
||||
beforeEach(async () => {
|
||||
const trialExpiryDate = DateTime.now().plus({ days: 30 }).toISODate();
|
||||
user = await createUser({ trialExpiryDate });
|
||||
token = createAuthTokenByUserId(user.id);
|
||||
|
||||
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(true);
|
||||
});
|
||||
|
||||
describe('should return in trial, active subscription and expire at info', () => {
|
||||
beforeEach(async () => {
|
||||
vi.spyOn(User.prototype, 'inTrial').mockResolvedValue(false);
|
||||
vi.spyOn(User.prototype, 'hasActiveSubscription').mockResolvedValue(true);
|
||||
});
|
||||
|
||||
it('should return null', async () => {
|
||||
const response = await request(app)
|
||||
.get(`/api/v1/users/${user.id}/trial`)
|
||||
.set('Authorization', token)
|
||||
.expect(200);
|
||||
|
||||
const expectedResponsePayload = await getUserTrialMock(user);
|
||||
expect(response.body).toEqual(expectedResponsePayload);
|
||||
});
|
||||
});
|
||||
});
|
16
packages/backend/src/controllers/api/v1/users/get-user.js
Normal file
16
packages/backend/src/controllers/api/v1/users/get-user.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { renderObject } from '../../../../helpers/renderer.js';
|
||||
import User from '../../../../models/user.js';
|
||||
|
||||
export default async (request, response) => {
|
||||
const user = await User.query()
|
||||
.leftJoinRelated({
|
||||
role: true,
|
||||
})
|
||||
.withGraphFetched({
|
||||
role: true,
|
||||
})
|
||||
.findById(request.params.userId)
|
||||
.throwIfNotFound();
|
||||
|
||||
renderObject(response, user);
|
||||
};
|
@@ -0,0 +1,36 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import request from 'supertest';
|
||||
import app from '../../../../app.js';
|
||||
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
|
||||
import { createUser } from '../../../../../test/factories/user';
|
||||
import { createPermission } from '../../../../../test/factories/permission';
|
||||
import getUserMock from '../../../../../test/mocks/rest/api/v1/users/get-user';
|
||||
|
||||
describe('GET /api/v1/users/:userId', () => {
|
||||
let currentUser, currentUserRole, anotherUser, anotherUserRole, token;
|
||||
|
||||
beforeEach(async () => {
|
||||
currentUser = await createUser();
|
||||
anotherUser = await createUser();
|
||||
currentUserRole = await currentUser.$relatedQuery('role');
|
||||
anotherUserRole = await anotherUser.$relatedQuery('role');
|
||||
|
||||
await createPermission({
|
||||
roleId: currentUserRole.id,
|
||||
action: 'read',
|
||||
subject: 'User',
|
||||
});
|
||||
|
||||
token = createAuthTokenByUserId(currentUser.id);
|
||||
});
|
||||
|
||||
it('should return specified user info', async () => {
|
||||
const response = await request(app)
|
||||
.get(`/api/v1/users/${anotherUser.id}`)
|
||||
.set('Authorization', token)
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = getUserMock(anotherUser, anotherUserRole);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
18
packages/backend/src/controllers/api/v1/users/get-users.js
Normal file
18
packages/backend/src/controllers/api/v1/users/get-users.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import { renderObject } from '../../../../helpers/renderer.js';
|
||||
import User from '../../../../models/user.js';
|
||||
import paginateRest from '../../../../helpers/pagination-rest.js';
|
||||
|
||||
export default async (request, response) => {
|
||||
const usersQuery = User.query()
|
||||
.leftJoinRelated({
|
||||
role: true,
|
||||
})
|
||||
.withGraphFetched({
|
||||
role: true,
|
||||
})
|
||||
.orderBy('full_name', 'asc');
|
||||
|
||||
const users = await paginateRest(usersQuery, request.query.page);
|
||||
|
||||
renderObject(response, users);
|
||||
};
|
@@ -0,0 +1,56 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import request from 'supertest';
|
||||
import app from '../../../../app';
|
||||
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
|
||||
import { createRole } from '../../../../../test/factories/role';
|
||||
import { createPermission } from '../../../../../test/factories/permission';
|
||||
import { createUser } from '../../../../../test/factories/user';
|
||||
import getUsersMock from '../../../../../test/mocks/rest/api/v1/users/get-users';
|
||||
|
||||
describe('GET /api/v1/users', () => {
|
||||
let currentUser, currentUserRole, anotherUser, anotherUserRole, token;
|
||||
|
||||
beforeEach(async () => {
|
||||
currentUserRole = await createRole({
|
||||
key: 'currentUser',
|
||||
name: 'Current user role',
|
||||
});
|
||||
|
||||
await createPermission({
|
||||
action: 'read',
|
||||
subject: 'User',
|
||||
roleId: currentUserRole.id,
|
||||
});
|
||||
|
||||
currentUser = await createUser({
|
||||
roleId: currentUserRole.id,
|
||||
fullName: 'Current User',
|
||||
});
|
||||
|
||||
anotherUserRole = await createRole({
|
||||
key: 'anotherUser',
|
||||
name: 'Another user role',
|
||||
});
|
||||
|
||||
anotherUser = await createUser({
|
||||
roleId: anotherUserRole.id,
|
||||
fullName: 'Another User',
|
||||
});
|
||||
|
||||
token = createAuthTokenByUserId(currentUser.id);
|
||||
});
|
||||
|
||||
it('should return users data', async () => {
|
||||
const response = await request(app)
|
||||
.get('/api/v1/users')
|
||||
.set('Authorization', token)
|
||||
.expect(200);
|
||||
|
||||
const expectedResponsePayload = await getUsersMock(
|
||||
[anotherUser, currentUser],
|
||||
[anotherUserRole, currentUserRole]
|
||||
);
|
||||
|
||||
expect(response.body).toEqual(expectedResponsePayload);
|
||||
});
|
||||
});
|
3
packages/backend/src/controllers/healthcheck/index.js
Normal file
3
packages/backend/src/controllers/healthcheck/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export default async (request, response) => {
|
||||
response.status(200).end();
|
||||
};
|
@@ -0,0 +1,9 @@
|
||||
import { describe, it } from 'vitest';
|
||||
import request from 'supertest';
|
||||
import app from '../../app.js';
|
||||
|
||||
describe('GET /healthcheck', () => {
|
||||
it('should return 200 response with version data', async () => {
|
||||
await request(app).get('/healthcheck').expect(200);
|
||||
});
|
||||
});
|
@@ -20,7 +20,8 @@ export const isAuthenticated = async (_parent, _args, req) => {
|
||||
.withGraphFetched({
|
||||
role: true,
|
||||
permissions: true,
|
||||
});
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
@@ -28,6 +29,14 @@ export const isAuthenticated = async (_parent, _args, req) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const authenticateUser = async (request, response, next) => {
|
||||
if (await isAuthenticated(null, null, request)) {
|
||||
next();
|
||||
} else {
|
||||
return response.status(401).end();
|
||||
}
|
||||
};
|
||||
|
||||
const isAuthenticatedRule = rule()(isAuthenticated);
|
||||
|
||||
export const authenticationRules = {
|
||||
|
@@ -1,11 +1,8 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { allow } from 'graphql-shield';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import User from '../models/user.js';
|
||||
import { isAuthenticated, authenticationRules } from './authentication.js';
|
||||
|
||||
vi.mock('jsonwebtoken');
|
||||
vi.mock('../models/user.js');
|
||||
import { createUser } from '../../test/factories/user.js';
|
||||
import createAuthTokenByUserId from '../helpers/create-auth-token-by-user-id.js';
|
||||
|
||||
describe('isAuthenticated', () => {
|
||||
it('should return false if no token is provided', async () => {
|
||||
@@ -14,29 +11,26 @@ describe('isAuthenticated', () => {
|
||||
});
|
||||
|
||||
it('should return false if token is invalid', async () => {
|
||||
jwt.verify.mockImplementation(() => {
|
||||
throw new Error('invalid token');
|
||||
});
|
||||
|
||||
const req = { headers: { authorization: 'invalidToken' } };
|
||||
expect(await isAuthenticated(null, null, req)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true if token is valid', async () => {
|
||||
jwt.verify.mockReturnValue({ userId: '123' });
|
||||
it('should return true if token is valid and there is a user', async () => {
|
||||
const user = await createUser();
|
||||
const token = createAuthTokenByUserId(user.id);
|
||||
|
||||
User.query.mockReturnValue({
|
||||
findById: vi.fn().mockReturnValue({
|
||||
leftJoinRelated: vi.fn().mockReturnThis(),
|
||||
withGraphFetched: vi
|
||||
.fn()
|
||||
.mockResolvedValue({ id: '123', role: {}, permissions: {} }),
|
||||
}),
|
||||
});
|
||||
|
||||
const req = { headers: { authorization: 'validToken' } };
|
||||
const req = { headers: { authorization: token } };
|
||||
expect(await isAuthenticated(null, null, req)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if token is valid and but there is no user', async () => {
|
||||
const user = await createUser();
|
||||
const token = createAuthTokenByUserId(user.id);
|
||||
await user.$query().delete();
|
||||
|
||||
const req = { headers: { authorization: token } };
|
||||
expect(await isAuthenticated(null, null, req)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('authentication rules', () => {
|
||||
|
22
packages/backend/src/helpers/authorization.js
Normal file
22
packages/backend/src/helpers/authorization.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const authorizationList = {
|
||||
'/api/v1/users/:userId': {
|
||||
action: 'read',
|
||||
subject: 'User',
|
||||
},
|
||||
'/api/v1/users/': {
|
||||
action: 'read',
|
||||
subject: 'User',
|
||||
},
|
||||
};
|
||||
|
||||
export const authorizeUser = async (request, response, next) => {
|
||||
const currentRoute = request.baseUrl + request.route.path;
|
||||
const currentRouteRule = authorizationList[currentRoute];
|
||||
|
||||
try {
|
||||
request.currentUser.can(currentRouteRule.action, currentRouteRule.subject);
|
||||
next();
|
||||
} catch (error) {
|
||||
return response.status(403).end();
|
||||
}
|
||||
};
|
11
packages/backend/src/helpers/check-is-cloud.js
Normal file
11
packages/backend/src/helpers/check-is-cloud.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import appConfig from '../config/app.js';
|
||||
|
||||
export const checkIsCloud = async (request, response, next) => {
|
||||
if (appConfig.isCloud) {
|
||||
next();
|
||||
} else {
|
||||
return response.status(404).end();
|
||||
}
|
||||
};
|
||||
|
||||
export default checkIsCloud;
|
25
packages/backend/src/helpers/pagination-rest.js
Normal file
25
packages/backend/src/helpers/pagination-rest.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const paginateRest = async (query, page) => {
|
||||
const pageSize = 10;
|
||||
|
||||
page = parseInt(page, 10);
|
||||
|
||||
if (isNaN(page) || page < 1) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
const [records, count] = await Promise.all([
|
||||
query.limit(pageSize).offset((page - 1) * pageSize),
|
||||
query.resultSize(),
|
||||
]);
|
||||
|
||||
return {
|
||||
pageInfo: {
|
||||
currentPage: page,
|
||||
totalPages: Math.ceil(count / pageSize),
|
||||
},
|
||||
totalCount: count,
|
||||
records,
|
||||
};
|
||||
};
|
||||
|
||||
export default paginateRest;
|
42
packages/backend/src/helpers/renderer.js
Normal file
42
packages/backend/src/helpers/renderer.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import serializers from '../serializers/index.js';
|
||||
|
||||
const isPaginated = (object) =>
|
||||
object?.pageInfo &&
|
||||
object?.totalCount !== undefined &&
|
||||
Array.isArray(object?.records);
|
||||
|
||||
const isArray = (object) =>
|
||||
Array.isArray(object) || Array.isArray(object?.records);
|
||||
|
||||
const totalCount = (object) =>
|
||||
isPaginated(object) ? object.totalCount : isArray(object) ? object.length : 1;
|
||||
|
||||
const renderObject = (response, object) => {
|
||||
let data = isPaginated(object) ? object.records : object;
|
||||
const type = isPaginated(object)
|
||||
? object.records[0].constructor.name
|
||||
: object.constructor.name;
|
||||
|
||||
const serializer = serializers[type];
|
||||
|
||||
if (serializer) {
|
||||
data = Array.isArray(data)
|
||||
? data.map((item) => serializer(item))
|
||||
: serializer(data);
|
||||
}
|
||||
|
||||
const computedPayload = {
|
||||
data,
|
||||
meta: {
|
||||
type,
|
||||
count: totalCount(object),
|
||||
isArray: isArray(object),
|
||||
currentPage: isPaginated(object) ? object.pageInfo.currentPage : null,
|
||||
totalPages: isPaginated(object) ? object.pageInfo.totalPages : null,
|
||||
},
|
||||
};
|
||||
|
||||
return response.json(computedPayload);
|
||||
};
|
||||
|
||||
export { renderObject };
|
8
packages/backend/src/routes/api/v1/automatisch.js
Normal file
8
packages/backend/src/routes/api/v1/automatisch.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Router } from 'express';
|
||||
import versionAction from '../../../controllers/api/v1/automatisch/version.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/version', versionAction);
|
||||
|
||||
export default router;
|
22
packages/backend/src/routes/api/v1/users.js
Normal file
22
packages/backend/src/routes/api/v1/users.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Router } from 'express';
|
||||
import { authenticateUser } from '../../../helpers/authentication.js';
|
||||
import { authorizeUser } from '../../../helpers/authorization.js';
|
||||
import checkIsCloud from '../../../helpers/check-is-cloud.js';
|
||||
import getCurrentUserAction from '../../../controllers/api/v1/users/get-current-user.js';
|
||||
import getUserAction from '../../../controllers/api/v1/users/get-user.js';
|
||||
import getUsersAction from '../../../controllers/api/v1/users/get-users.js';
|
||||
import getUserTrialAction from '../../../controllers/api/v1/users/get-user-trial.ee.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/', authenticateUser, authorizeUser, getUsersAction);
|
||||
router.get('/me', authenticateUser, getCurrentUserAction);
|
||||
router.get('/:userId', authenticateUser, authorizeUser, getUserAction);
|
||||
router.get(
|
||||
'/:userId/trial',
|
||||
authenticateUser,
|
||||
checkIsCloud,
|
||||
getUserTrialAction
|
||||
);
|
||||
|
||||
export default router;
|
8
packages/backend/src/routes/healthcheck.js
Normal file
8
packages/backend/src/routes/healthcheck.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Router } from 'express';
|
||||
import indexAction from '../controllers/healthcheck/index.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/', indexAction);
|
||||
|
||||
export default router;
|
@@ -2,11 +2,17 @@ import { Router } from 'express';
|
||||
import graphQLInstance from '../helpers/graphql-instance.js';
|
||||
import webhooksRouter from './webhooks.js';
|
||||
import paddleRouter from './paddle.ee.js';
|
||||
import healthcheckRouter from './healthcheck.js';
|
||||
import automatischRouter from './api/v1/automatisch.js';
|
||||
import usersRouter from './api/v1/users.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.use('/graphql', graphQLInstance);
|
||||
router.use('/webhooks', webhooksRouter);
|
||||
router.use('/paddle', paddleRouter);
|
||||
router.use('/healthcheck', healthcheckRouter);
|
||||
router.use('/api/v1/automatisch', automatischRouter);
|
||||
router.use('/api/v1/users', usersRouter);
|
||||
|
||||
export default router;
|
||||
|
11
packages/backend/src/serializers/index.js
Normal file
11
packages/backend/src/serializers/index.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import userSerializer from './user.js';
|
||||
import roleSerializer from './role.js';
|
||||
import permissionSerializer from './permission.js';
|
||||
|
||||
const serializers = {
|
||||
User: userSerializer,
|
||||
Role: roleSerializer,
|
||||
Permission: permissionSerializer,
|
||||
};
|
||||
|
||||
export default serializers;
|
13
packages/backend/src/serializers/permission.js
Normal file
13
packages/backend/src/serializers/permission.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const permissionSerializer = (permission) => {
|
||||
return {
|
||||
id: permission.id,
|
||||
roleId: permission.roleId,
|
||||
action: permission.action,
|
||||
subject: permission.subject,
|
||||
conditions: permission.conditions,
|
||||
createdAt: permission.createdAt,
|
||||
updatedAt: permission.updatedAt,
|
||||
};
|
||||
};
|
||||
|
||||
export default permissionSerializer;
|
13
packages/backend/src/serializers/role.js
Normal file
13
packages/backend/src/serializers/role.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const roleSerializer = (role) => {
|
||||
return {
|
||||
id: role.id,
|
||||
name: role.name,
|
||||
key: role.key,
|
||||
description: role.description,
|
||||
createdAt: role.createdAt,
|
||||
updatedAt: role.updatedAt,
|
||||
isAdmin: role.isAdmin,
|
||||
};
|
||||
};
|
||||
|
||||
export default roleSerializer;
|
32
packages/backend/src/serializers/user.js
Normal file
32
packages/backend/src/serializers/user.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import roleSerializer from './role.js';
|
||||
import permissionSerializer from './permission.js';
|
||||
import appConfig from '../config/app.js';
|
||||
|
||||
const userSerializer = (user) => {
|
||||
let userData = {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
createdAt: user.createdAt,
|
||||
updatedAt: user.updatedAt,
|
||||
fullName: user.fullName,
|
||||
roleId: user.roleId,
|
||||
};
|
||||
|
||||
if (user.role) {
|
||||
userData.role = roleSerializer(user.role);
|
||||
}
|
||||
|
||||
if (user.permissions) {
|
||||
userData.permissions = user.permissions.map((permission) =>
|
||||
permissionSerializer(permission)
|
||||
);
|
||||
}
|
||||
|
||||
if (appConfig.isCloud && user.trialExpiryDate) {
|
||||
userData.trialExpiryDate = user.trialExpiryDate;
|
||||
}
|
||||
|
||||
return userData;
|
||||
};
|
||||
|
||||
export default userSerializer;
|
@@ -1,4 +1,5 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
import Config from '../../src/models/config';
|
||||
|
||||
export const createConfig = async (params = {}) => {
|
||||
const configData = {
|
||||
@@ -6,10 +7,7 @@ export const createConfig = async (params = {}) => {
|
||||
value: params?.value || { data: 'sampleConfig' },
|
||||
};
|
||||
|
||||
const [config] = await global.knex
|
||||
.table('config')
|
||||
.insert(configData)
|
||||
.returning('*');
|
||||
const config = await Config.query().insert(configData).returning('*');
|
||||
|
||||
return config;
|
||||
};
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import appConfig from '../../src/config/app';
|
||||
import { AES } from 'crypto-js';
|
||||
import Connection from '../../src/models/connection';
|
||||
|
||||
export const createConnection = async (params = {}) => {
|
||||
params.key = params?.key || 'deepl';
|
||||
@@ -16,10 +17,7 @@ export const createConnection = async (params = {}) => {
|
||||
appConfig.encryptionKey
|
||||
).toString();
|
||||
|
||||
const [connection] = await global.knex
|
||||
.table('connections')
|
||||
.insert(params)
|
||||
.returning('*');
|
||||
const connection = await Connection.query().insert(params).returning('*');
|
||||
|
||||
return connection;
|
||||
};
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import ExecutionStep from '../../src/models/execution-step';
|
||||
import { createExecution } from './execution';
|
||||
import { createStep } from './step';
|
||||
|
||||
@@ -8,8 +9,7 @@ export const createExecutionStep = async (params = {}) => {
|
||||
params.dataIn = params?.dataIn || { dataIn: 'dataIn' };
|
||||
params.dataOut = params?.dataOut || { dataOut: 'dataOut' };
|
||||
|
||||
const [executionStep] = await global.knex
|
||||
.table('executionSteps')
|
||||
const executionStep = await ExecutionStep.query()
|
||||
.insert(params)
|
||||
.returning('*');
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import Execution from '../../src/models/execution';
|
||||
import { createFlow } from './flow';
|
||||
|
||||
export const createExecution = async (params = {}) => {
|
||||
@@ -6,10 +7,7 @@ export const createExecution = async (params = {}) => {
|
||||
params.createdAt = params?.createdAt || new Date().toISOString();
|
||||
params.updatedAt = params?.updatedAt || new Date().toISOString();
|
||||
|
||||
const [execution] = await global.knex
|
||||
.table('executions')
|
||||
.insert(params)
|
||||
.returning('*');
|
||||
const execution = await Execution.query().insert(params).returning('*');
|
||||
|
||||
return execution;
|
||||
};
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import Flow from '../../src/models/flow';
|
||||
import { createUser } from './user';
|
||||
|
||||
export const createFlow = async (params = {}) => {
|
||||
@@ -6,7 +7,7 @@ export const createFlow = async (params = {}) => {
|
||||
params.createdAt = params?.createdAt || new Date().toISOString();
|
||||
params.updatedAt = params?.updatedAt || new Date().toISOString();
|
||||
|
||||
const [flow] = await global.knex.table('flows').insert(params).returning('*');
|
||||
const flow = await Flow.query().insert(params).returning('*');
|
||||
|
||||
return flow;
|
||||
};
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import Permission from '../../src/models/permission';
|
||||
import { createRole } from './role';
|
||||
|
||||
export const createPermission = async (params = {}) => {
|
||||
@@ -6,10 +7,7 @@ export const createPermission = async (params = {}) => {
|
||||
params.subject = params?.subject || 'User';
|
||||
params.conditions = params?.conditions || ['isCreator'];
|
||||
|
||||
const [permission] = await global.knex
|
||||
.table('permissions')
|
||||
.insert(params)
|
||||
.returning('*');
|
||||
const permission = await Permission.query().insert(params).returning('*');
|
||||
|
||||
return permission;
|
||||
};
|
||||
|
@@ -1,8 +1,10 @@
|
||||
import Role from '../../src/models/role';
|
||||
|
||||
export const createRole = async (params = {}) => {
|
||||
params.name = params?.name || 'Viewer';
|
||||
params.key = params?.key || 'viewer';
|
||||
|
||||
const [role] = await global.knex.table('roles').insert(params).returning('*');
|
||||
const role = await Role.query().insert(params).returning('*');
|
||||
|
||||
return role;
|
||||
};
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import Step from '../../src/models/step';
|
||||
import { createFlow } from './flow';
|
||||
|
||||
export const createStep = async (params = {}) => {
|
||||
@@ -16,7 +17,7 @@ export const createStep = async (params = {}) => {
|
||||
params.appKey =
|
||||
params?.appKey || (params.type === 'action' ? 'deepl' : 'webhook');
|
||||
|
||||
const [step] = await global.knex.table('steps').insert(params).returning('*');
|
||||
const step = await Step.query().insert(params).returning('*');
|
||||
|
||||
return step;
|
||||
};
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { createRole } from './role';
|
||||
import { faker } from '@faker-js/faker';
|
||||
import User from '../../src/models/user';
|
||||
|
||||
export const createUser = async (params = {}) => {
|
||||
params.roleId = params?.roleId || (await createRole()).id;
|
||||
@@ -7,7 +8,7 @@ export const createUser = async (params = {}) => {
|
||||
params.email = params?.email || faker.internet.email();
|
||||
params.password = params?.password || faker.internet.password();
|
||||
|
||||
const [user] = await global.knex.table('users').insert(params).returning('*');
|
||||
const user = await User.query().insert(params).returning('*');
|
||||
|
||||
return user;
|
||||
};
|
||||
|
@@ -0,0 +1,32 @@
|
||||
const getCurrentUserMock = (currentUser, role) => {
|
||||
return {
|
||||
data: {
|
||||
createdAt: currentUser.createdAt.toISOString(),
|
||||
email: currentUser.email,
|
||||
fullName: currentUser.fullName,
|
||||
id: currentUser.id,
|
||||
permissions: [],
|
||||
role: {
|
||||
createdAt: role.createdAt.toISOString(),
|
||||
description: null,
|
||||
id: role.id,
|
||||
isAdmin: role.isAdmin,
|
||||
key: role.key,
|
||||
name: role.name,
|
||||
updatedAt: role.updatedAt.toISOString(),
|
||||
},
|
||||
roleId: role.id,
|
||||
trialExpiryDate: currentUser.trialExpiryDate.toISOString(),
|
||||
updatedAt: currentUser.updatedAt.toISOString(),
|
||||
},
|
||||
meta: {
|
||||
count: 1,
|
||||
currentPage: null,
|
||||
isArray: false,
|
||||
totalPages: null,
|
||||
type: 'User',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default getCurrentUserMock;
|
@@ -0,0 +1,17 @@
|
||||
const getUserTrialMock = async (currentUser) => {
|
||||
return {
|
||||
data: {
|
||||
inTrial: await currentUser.inTrial(),
|
||||
expireAt: currentUser.trialExpiryDate.toISOString(),
|
||||
},
|
||||
meta: {
|
||||
count: 1,
|
||||
currentPage: null,
|
||||
isArray: false,
|
||||
totalPages: null,
|
||||
type: 'Object',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default getUserTrialMock;
|
31
packages/backend/test/mocks/rest/api/v1/users/get-user.js
Normal file
31
packages/backend/test/mocks/rest/api/v1/users/get-user.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const getUserMock = (currentUser, role) => {
|
||||
return {
|
||||
data: {
|
||||
createdAt: currentUser.createdAt.toISOString(),
|
||||
email: currentUser.email,
|
||||
fullName: currentUser.fullName,
|
||||
id: currentUser.id,
|
||||
role: {
|
||||
createdAt: role.createdAt.toISOString(),
|
||||
description: null,
|
||||
id: role.id,
|
||||
isAdmin: role.isAdmin,
|
||||
key: role.key,
|
||||
name: role.name,
|
||||
updatedAt: role.updatedAt.toISOString(),
|
||||
},
|
||||
roleId: role.id,
|
||||
trialExpiryDate: currentUser.trialExpiryDate.toISOString(),
|
||||
updatedAt: currentUser.updatedAt.toISOString(),
|
||||
},
|
||||
meta: {
|
||||
count: 1,
|
||||
currentPage: null,
|
||||
isArray: false,
|
||||
totalPages: null,
|
||||
type: 'User',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default getUserMock;
|
38
packages/backend/test/mocks/rest/api/v1/users/get-users.js
Normal file
38
packages/backend/test/mocks/rest/api/v1/users/get-users.js
Normal file
@@ -0,0 +1,38 @@
|
||||
const getUsersMock = async (users, roles) => {
|
||||
const data = users.map((user) => {
|
||||
const role = roles.find((r) => r.id === user.roleId);
|
||||
return {
|
||||
createdAt: user.createdAt.toISOString(),
|
||||
email: user.email,
|
||||
fullName: user.fullName,
|
||||
id: user.id,
|
||||
role: role
|
||||
? {
|
||||
createdAt: role.createdAt.toISOString(),
|
||||
description: role.description,
|
||||
id: role.id,
|
||||
isAdmin: role.isAdmin,
|
||||
key: role.key,
|
||||
name: role.name,
|
||||
updatedAt: role.updatedAt.toISOString(),
|
||||
}
|
||||
: null, // Fallback to null if role not found
|
||||
roleId: user.roleId,
|
||||
trialExpiryDate: user.trialExpiryDate.toISOString(),
|
||||
updatedAt: user.updatedAt.toISOString(),
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
data: data,
|
||||
meta: {
|
||||
count: data.length,
|
||||
currentPage: 1,
|
||||
isArray: true,
|
||||
totalPages: 1,
|
||||
type: 'User',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default getUsersMock;
|
87
yarn.lock
87
yarn.lock
@@ -3952,11 +3952,6 @@
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz"
|
||||
integrity sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==
|
||||
|
||||
"@types/node@>=8.1.0":
|
||||
version "18.14.5"
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-18.14.5.tgz"
|
||||
integrity sha512-CRT4tMK/DHYhw1fcCEBwME9CSaZNclxfzVMe7GsO6ULSwsttbj70wSiX6rZdIjGblu93sTJxLdhNIT85KKI7Qw==
|
||||
|
||||
"@types/node@^12.0.0":
|
||||
version "12.20.42"
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz"
|
||||
@@ -6372,19 +6367,6 @@ cookiejar@^2.1.4:
|
||||
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b"
|
||||
integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==
|
||||
|
||||
copyfiles@^2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz"
|
||||
integrity sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==
|
||||
dependencies:
|
||||
glob "^7.0.5"
|
||||
minimatch "^3.0.3"
|
||||
mkdirp "^1.0.4"
|
||||
noms "0.0.0"
|
||||
through2 "^2.0.1"
|
||||
untildify "^4.0.0"
|
||||
yargs "^16.1.0"
|
||||
|
||||
core-js-compat@^3.20.0, core-js-compat@^3.20.2:
|
||||
version "3.20.3"
|
||||
resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.20.3.tgz"
|
||||
@@ -8655,7 +8637,7 @@ glob-to-regexp@^0.4.1:
|
||||
resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz"
|
||||
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
|
||||
|
||||
glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
|
||||
glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz"
|
||||
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
||||
@@ -8791,11 +8773,6 @@ graphql-tools@^8.2.0:
|
||||
optionalDependencies:
|
||||
"@apollo/client" "~3.2.5 || ~3.3.0 || ~3.4.0"
|
||||
|
||||
graphql-type-json@^0.3.2:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.npmjs.org/graphql-type-json/-/graphql-type-json-0.3.2.tgz"
|
||||
integrity sha512-J+vjof74oMlCWXSvt0DOf2APEdZOCdubEvGDUAlqH//VBYcOYsGgRW7Xzorr44LvkjiuvecWc8fChxuZZbChtg==
|
||||
|
||||
graphql@^15.6.0:
|
||||
version "15.8.0"
|
||||
resolved "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz"
|
||||
@@ -9249,7 +9226,7 @@ inflight@^1.0.4:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3:
|
||||
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
@@ -9671,11 +9648,6 @@ is-yarn-global@^0.3.0:
|
||||
resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz"
|
||||
integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==
|
||||
|
||||
isarray@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
|
||||
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
|
||||
|
||||
isarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
|
||||
@@ -11060,13 +11032,6 @@ minimatch@3.0.4, minimatch@^3.0.4:
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimatch@^3.0.3:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz"
|
||||
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimatch@^5.0.1:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz"
|
||||
@@ -11493,10 +11458,10 @@ node-releases@^2.0.1:
|
||||
resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz"
|
||||
integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==
|
||||
|
||||
nodemailer@6.7.0:
|
||||
version "6.7.0"
|
||||
resolved "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.0.tgz"
|
||||
integrity sha512-AtiTVUFHLiiDnMQ43zi0YgkzHOEWUkhDgPlBXrsDzJiJvB29Alo4OKxHQ0ugF3gRqRQIneCLtZU3yiUo7pItZw==
|
||||
nodemailer@6.9.9:
|
||||
version "6.9.9"
|
||||
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.9.tgz#4549bfbf710cc6addec5064dd0f19874d24248d9"
|
||||
integrity sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==
|
||||
|
||||
nodemon@^2.0.13:
|
||||
version "2.0.15"
|
||||
@@ -11514,14 +11479,6 @@ nodemon@^2.0.13:
|
||||
undefsafe "^2.0.5"
|
||||
update-notifier "^5.1.0"
|
||||
|
||||
noms@0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz"
|
||||
integrity sha1-2o69nzr51nYJGbJ9nNyAkqczKFk=
|
||||
dependencies:
|
||||
inherits "^2.0.1"
|
||||
readable-stream "~1.0.31"
|
||||
|
||||
nopt@^4.0.1:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz"
|
||||
@@ -13635,16 +13592,6 @@ readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stream@^2.2.2, readable
|
||||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
readable-stream@~1.0.31:
|
||||
version "1.0.34"
|
||||
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz"
|
||||
integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.1"
|
||||
isarray "0.0.1"
|
||||
string_decoder "~0.10.x"
|
||||
|
||||
readdir-scoped-modules@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz"
|
||||
@@ -14771,11 +14718,6 @@ string_decoder@^1.1.1:
|
||||
dependencies:
|
||||
safe-buffer "~5.2.0"
|
||||
|
||||
string_decoder@~0.10.x:
|
||||
version "0.10.31"
|
||||
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
|
||||
integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=
|
||||
|
||||
string_decoder@~1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz"
|
||||
@@ -14862,14 +14804,6 @@ strip-literal@^1.3.0:
|
||||
dependencies:
|
||||
acorn "^8.10.0"
|
||||
|
||||
stripe@^11.13.0:
|
||||
version "11.13.0"
|
||||
resolved "https://registry.npmjs.org/stripe/-/stripe-11.13.0.tgz"
|
||||
integrity sha512-Jx0nDbdvRsTtDSX5OFQ+4rLmYIftoiOE9HAXWIgyhAz1QjRFI3UIiJ/kCyhkdJBoHu019O5Ya6EmQ5Zf635XDw==
|
||||
dependencies:
|
||||
"@types/node" ">=8.1.0"
|
||||
qs "^6.11.0"
|
||||
|
||||
strnum@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz"
|
||||
@@ -15167,7 +15101,7 @@ throat@^6.0.1:
|
||||
resolved "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz"
|
||||
integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==
|
||||
|
||||
through2@^2.0.0, through2@^2.0.1:
|
||||
through2@^2.0.0:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz"
|
||||
integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
|
||||
@@ -15572,11 +15506,6 @@ unquote@~1.1.1:
|
||||
resolved "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz"
|
||||
integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=
|
||||
|
||||
untildify@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz"
|
||||
integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
|
||||
|
||||
upath@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz"
|
||||
@@ -16496,7 +16425,7 @@ yargs-parser@^20.2.2, yargs-parser@^20.2.3:
|
||||
resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz"
|
||||
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||
|
||||
yargs@^16.1.0, yargs@^16.2.0:
|
||||
yargs@^16.2.0:
|
||||
version "16.2.0"
|
||||
resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz"
|
||||
integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
|
||||
|
Reference in New Issue
Block a user