mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-09 22:24:05 -08:00
feat(core): Add list query middleware to credentials (#7041)
This commit is contained in:
parent
413e0bccb4
commit
fd78021b68
|
@ -40,7 +40,7 @@ import type { RoleNames } from '@db/entities/Role';
|
||||||
import { RoleService } from './services/role.service';
|
import { RoleService } from './services/role.service';
|
||||||
import { ExecutionRepository, RoleRepository } from './databases/repositories';
|
import { ExecutionRepository, RoleRepository } from './databases/repositories';
|
||||||
import { VariablesService } from './environments/variables/variables.service';
|
import { VariablesService } from './environments/variables/variables.service';
|
||||||
import type { Credentials } from './requests';
|
import type { CredentialsEntity } from './databases/entities/CredentialsEntity';
|
||||||
|
|
||||||
const ERROR_TRIGGER_TYPE = config.getEnv('nodes.errorTriggerType');
|
const ERROR_TRIGGER_TYPE = config.getEnv('nodes.errorTriggerType');
|
||||||
|
|
||||||
|
@ -539,7 +539,7 @@ export function getNodesWithInaccessibleCreds(workflow: WorkflowEntity, userCred
|
||||||
export function validateWorkflowCredentialUsage(
|
export function validateWorkflowCredentialUsage(
|
||||||
newWorkflowVersion: WorkflowEntity,
|
newWorkflowVersion: WorkflowEntity,
|
||||||
previousWorkflowVersion: WorkflowEntity,
|
previousWorkflowVersion: WorkflowEntity,
|
||||||
credentialsUserHasAccessTo: Credentials.WithOwnedByAndSharedWith[],
|
credentialsUserHasAccessTo: CredentialsEntity[],
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* We only need to check nodes that use credentials the current user cannot access,
|
* We only need to check nodes that use credentials the current user cannot access,
|
||||||
|
|
|
@ -10,9 +10,10 @@ import { EECredentialsController } from './credentials.controller.ee';
|
||||||
import { CredentialsService } from './credentials.service';
|
import { CredentialsService } from './credentials.service';
|
||||||
|
|
||||||
import type { ICredentialsDb } from '@/Interfaces';
|
import type { ICredentialsDb } from '@/Interfaces';
|
||||||
import type { CredentialRequest } from '@/requests';
|
import type { CredentialRequest, ListQuery } from '@/requests';
|
||||||
import { Container } from 'typedi';
|
import { Container } from 'typedi';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
|
import { listQueryMiddleware } from '@/middlewares';
|
||||||
|
|
||||||
export const credentialsController = express.Router();
|
export const credentialsController = express.Router();
|
||||||
|
|
||||||
|
@ -35,8 +36,9 @@ credentialsController.use('/', EECredentialsController);
|
||||||
*/
|
*/
|
||||||
credentialsController.get(
|
credentialsController.get(
|
||||||
'/',
|
'/',
|
||||||
ResponseHelper.send(async (req: CredentialRequest.GetAll) => {
|
listQueryMiddleware,
|
||||||
return CredentialsService.getMany(req.user);
|
ResponseHelper.send(async (req: ListQuery.Request) => {
|
||||||
|
return CredentialsService.getMany(req.user, { listQueryOptions: req.listQueryOptions });
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import type {
|
||||||
import { CREDENTIAL_EMPTY_VALUE, deepCopy, LoggerProxy, NodeHelpers } from 'n8n-workflow';
|
import { CREDENTIAL_EMPTY_VALUE, deepCopy, LoggerProxy, NodeHelpers } from 'n8n-workflow';
|
||||||
import { Container } from 'typedi';
|
import { Container } from 'typedi';
|
||||||
import type { FindManyOptions, FindOptionsWhere } from 'typeorm';
|
import type { FindManyOptions, FindOptionsWhere } from 'typeorm';
|
||||||
import { In } from 'typeorm';
|
import { In, Like } from 'typeorm';
|
||||||
|
|
||||||
import * as Db from '@/Db';
|
import * as Db from '@/Db';
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
import * as ResponseHelper from '@/ResponseHelper';
|
||||||
|
@ -21,7 +21,7 @@ import { SharedCredentials } from '@db/entities/SharedCredentials';
|
||||||
import { validateEntity } from '@/GenericHelpers';
|
import { validateEntity } from '@/GenericHelpers';
|
||||||
import { ExternalHooks } from '@/ExternalHooks';
|
import { ExternalHooks } from '@/ExternalHooks';
|
||||||
import type { User } from '@db/entities/User';
|
import type { User } from '@db/entities/User';
|
||||||
import type { CredentialRequest } from '@/requests';
|
import type { CredentialRequest, ListQuery } from '@/requests';
|
||||||
import { CredentialTypes } from '@/CredentialTypes';
|
import { CredentialTypes } from '@/CredentialTypes';
|
||||||
import { RoleService } from '@/services/role.service';
|
import { RoleService } from '@/services/role.service';
|
||||||
import { OwnershipService } from '@/services/ownership.service';
|
import { OwnershipService } from '@/services/ownership.service';
|
||||||
|
@ -37,33 +37,70 @@ export class CredentialsService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getMany(user: User, options?: { disableGlobalRole: boolean }) {
|
private static toFindManyOptions(listQueryOptions?: ListQuery.Options) {
|
||||||
type Select = Array<keyof ICredentialsDb>;
|
const findManyOptions: FindManyOptions<CredentialsEntity> = {};
|
||||||
|
|
||||||
const select: Select = ['id', 'name', 'type', 'nodesAccess', 'createdAt', 'updatedAt'];
|
type Select = Array<keyof CredentialsEntity>;
|
||||||
|
|
||||||
const relations = ['shared', 'shared.role', 'shared.user'];
|
const defaultRelations = ['shared', 'shared.role', 'shared.user'];
|
||||||
|
const defaultSelect: Select = ['id', 'name', 'type', 'nodesAccess', 'createdAt', 'updatedAt'];
|
||||||
|
|
||||||
const returnAll = user.globalRole.name === 'owner' && options?.disableGlobalRole !== true;
|
if (!listQueryOptions) return { select: defaultSelect, relations: defaultRelations };
|
||||||
|
|
||||||
const addOwnedByAndSharedWith = (c: CredentialsEntity) =>
|
const { filter, select, take, skip } = listQueryOptions;
|
||||||
Container.get(OwnershipService).addOwnedByAndSharedWith(c);
|
|
||||||
|
|
||||||
if (returnAll) {
|
if (typeof filter?.name === 'string' && filter?.name !== '') {
|
||||||
const credentials = await Db.collections.Credentials.find({ select, relations });
|
filter.name = Like(`%${filter.name}%`);
|
||||||
|
|
||||||
return credentials.map(addOwnedByAndSharedWith);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ids = await CredentialsService.getAccessibleCredentials(user.id);
|
if (typeof filter?.type === 'string' && filter?.type !== '') {
|
||||||
|
filter.type = Like(`%${filter.type}%`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter) findManyOptions.where = filter;
|
||||||
|
if (select) findManyOptions.select = select;
|
||||||
|
if (take) findManyOptions.take = take;
|
||||||
|
if (skip) findManyOptions.skip = skip;
|
||||||
|
|
||||||
|
if (take && select && !select?.id) {
|
||||||
|
findManyOptions.select = { ...findManyOptions.select, id: true }; // pagination requires id
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!findManyOptions.select) {
|
||||||
|
findManyOptions.select = defaultSelect;
|
||||||
|
findManyOptions.relations = defaultRelations;
|
||||||
|
}
|
||||||
|
|
||||||
|
return findManyOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static addOwnedByAndSharedWith(credentials: CredentialsEntity[]) {
|
||||||
|
return credentials.map((c) => Container.get(OwnershipService).addOwnedByAndSharedWith(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getMany(
|
||||||
|
user: User,
|
||||||
|
options: { listQueryOptions?: ListQuery.Options; onlyOwn?: boolean } = {},
|
||||||
|
) {
|
||||||
|
const findManyOptions = this.toFindManyOptions(options.listQueryOptions);
|
||||||
|
|
||||||
|
const returnAll = user.globalRole.name === 'owner' && !options.onlyOwn;
|
||||||
|
const isDefaultSelect = !options.listQueryOptions?.select;
|
||||||
|
|
||||||
|
if (returnAll) {
|
||||||
|
const credentials = await Db.collections.Credentials.find(findManyOptions);
|
||||||
|
|
||||||
|
return isDefaultSelect ? this.addOwnedByAndSharedWith(credentials) : credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ids = await this.getAccessibleCredentials(user.id);
|
||||||
|
|
||||||
const credentials = await Db.collections.Credentials.find({
|
const credentials = await Db.collections.Credentials.find({
|
||||||
select,
|
...findManyOptions,
|
||||||
relations,
|
where: { ...findManyOptions.where, id: In(ids) }, // only accessible credentials
|
||||||
where: { id: In(ids) },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return credentials.map(addOwnedByAndSharedWith);
|
return isDefaultSelect ? this.addOwnedByAndSharedWith(credentials) : credentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { IsOptional, IsString } from 'class-validator';
|
||||||
|
import { Expose } from 'class-transformer';
|
||||||
|
import { BaseFilter } from './base.filter.dto';
|
||||||
|
|
||||||
|
export class CredentialsFilter extends BaseFilter {
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
@Expose()
|
||||||
|
name?: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
@Expose()
|
||||||
|
type?: string;
|
||||||
|
|
||||||
|
static async fromString(rawFilter: string) {
|
||||||
|
return this.toFilter(rawFilter, CredentialsFilter);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { BaseSelect } from './base.select.dto';
|
||||||
|
|
||||||
|
export class CredentialsSelect extends BaseSelect {
|
||||||
|
static get selectableFields() {
|
||||||
|
return new Set([
|
||||||
|
'id', // always included downstream
|
||||||
|
'name',
|
||||||
|
'type',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromString(rawFilter: string) {
|
||||||
|
return this.toSelect(rawFilter, CredentialsSelect);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
import * as ResponseHelper from '@/ResponseHelper';
|
||||||
import { WorkflowFilter } from './dtos/workflow.filter.dto';
|
import { WorkflowFilter } from './dtos/workflow.filter.dto';
|
||||||
|
import { CredentialsFilter } from './dtos/credentials.filter.dto';
|
||||||
import { UserFilter } from './dtos/user.filter.dto';
|
import { UserFilter } from './dtos/user.filter.dto';
|
||||||
import { toError } from '@/utils';
|
import { toError } from '@/utils';
|
||||||
|
|
||||||
|
@ -21,6 +22,8 @@ export const filterListQueryMiddleware = async (
|
||||||
|
|
||||||
if (req.baseUrl.endsWith('workflows')) {
|
if (req.baseUrl.endsWith('workflows')) {
|
||||||
Filter = WorkflowFilter;
|
Filter = WorkflowFilter;
|
||||||
|
} else if (req.baseUrl.endsWith('credentials')) {
|
||||||
|
Filter = CredentialsFilter;
|
||||||
} else if (req.baseUrl.endsWith('users')) {
|
} else if (req.baseUrl.endsWith('users')) {
|
||||||
Filter = UserFilter;
|
Filter = UserFilter;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import { WorkflowSelect } from './dtos/workflow.select.dto';
|
import { WorkflowSelect } from './dtos/workflow.select.dto';
|
||||||
import { UserSelect } from './dtos/user.select.dto';
|
import { UserSelect } from './dtos/user.select.dto';
|
||||||
|
import { CredentialsSelect } from './dtos/credentials.select.dto';
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
import * as ResponseHelper from '@/ResponseHelper';
|
||||||
import { toError } from '@/utils';
|
import { toError } from '@/utils';
|
||||||
|
|
||||||
|
@ -17,6 +18,8 @@ export const selectListQueryMiddleware: RequestHandler = (req: ListQuery.Request
|
||||||
|
|
||||||
if (req.baseUrl.endsWith('workflows')) {
|
if (req.baseUrl.endsWith('workflows')) {
|
||||||
Select = WorkflowSelect;
|
Select = WorkflowSelect;
|
||||||
|
} else if (req.baseUrl.endsWith('credentials')) {
|
||||||
|
Select = CredentialsSelect;
|
||||||
} else if (req.baseUrl.endsWith('users')) {
|
} else if (req.baseUrl.endsWith('users')) {
|
||||||
Select = UserSelect;
|
Select = UserSelect;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { CredentialsService } from '@/credentials/credentials.service';
|
||||||
import { NodeOperationError } from 'n8n-workflow';
|
import { NodeOperationError } from 'n8n-workflow';
|
||||||
import { RoleService } from '@/services/role.service';
|
import { RoleService } from '@/services/role.service';
|
||||||
import Container from 'typedi';
|
import Container from 'typedi';
|
||||||
|
import type { CredentialsEntity } from '@/databases/entities/CredentialsEntity';
|
||||||
import type { Credentials } from '@/requests';
|
import type { Credentials } from '@/requests';
|
||||||
|
|
||||||
export class EEWorkflowsService extends WorkflowsService {
|
export class EEWorkflowsService extends WorkflowsService {
|
||||||
|
@ -106,9 +107,7 @@ export class EEWorkflowsService extends WorkflowsService {
|
||||||
currentUser: User,
|
currentUser: User,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
workflow.usedCredentials = [];
|
workflow.usedCredentials = [];
|
||||||
const userCredentials = await CredentialsService.getMany(currentUser, {
|
const userCredentials = await CredentialsService.getMany(currentUser, { onlyOwn: true });
|
||||||
disableGlobalRole: true,
|
|
||||||
});
|
|
||||||
const credentialIdsUsedByWorkflow = new Set<string>();
|
const credentialIdsUsedByWorkflow = new Set<string>();
|
||||||
workflow.nodes.forEach((node) => {
|
workflow.nodes.forEach((node) => {
|
||||||
if (!node.credentials) {
|
if (!node.credentials) {
|
||||||
|
@ -151,7 +150,7 @@ export class EEWorkflowsService extends WorkflowsService {
|
||||||
|
|
||||||
static validateCredentialPermissionsToUser(
|
static validateCredentialPermissionsToUser(
|
||||||
workflow: WorkflowEntity,
|
workflow: WorkflowEntity,
|
||||||
allowedCredentials: Credentials.WithOwnedByAndSharedWith[],
|
allowedCredentials: CredentialsEntity[],
|
||||||
) {
|
) {
|
||||||
workflow.nodes.forEach((node) => {
|
workflow.nodes.forEach((node) => {
|
||||||
if (!node.credentials) {
|
if (!node.credentials) {
|
||||||
|
|
297
packages/cli/test/integration/credentials.controller.test.ts
Normal file
297
packages/cli/test/integration/credentials.controller.test.ts
Normal file
|
@ -0,0 +1,297 @@
|
||||||
|
import * as testDb from './shared/testDb';
|
||||||
|
import * as utils from './shared/utils/';
|
||||||
|
import { randomCredentialPayload as payload } from './shared/random';
|
||||||
|
|
||||||
|
import type { Credentials } from '@/requests';
|
||||||
|
import type { User } from '@db/entities/User';
|
||||||
|
|
||||||
|
const { any } = expect;
|
||||||
|
|
||||||
|
const testServer = utils.setupTestServer({ endpointGroups: ['credentials'] });
|
||||||
|
|
||||||
|
let owner: User;
|
||||||
|
let member: User;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await testDb.truncate(['SharedCredentials', 'Credentials']);
|
||||||
|
|
||||||
|
owner = await testDb.createOwner();
|
||||||
|
member = await testDb.createMember();
|
||||||
|
});
|
||||||
|
|
||||||
|
type GetAllResponse = { body: { data: Credentials.WithOwnedByAndSharedWith[] } };
|
||||||
|
|
||||||
|
describe('GET /credentials', () => {
|
||||||
|
describe('should return', () => {
|
||||||
|
test('all credentials for owner', async () => {
|
||||||
|
const role = await testDb.getCredentialOwnerRole();
|
||||||
|
|
||||||
|
const { id: id1 } = await testDb.saveCredential(payload(), { user: owner, role });
|
||||||
|
const { id: id2 } = await testDb.saveCredential(payload(), { user: member, role });
|
||||||
|
|
||||||
|
const response: GetAllResponse = await testServer
|
||||||
|
.authAgentFor(owner)
|
||||||
|
.get('/credentials')
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(response.body.data).toHaveLength(2);
|
||||||
|
|
||||||
|
response.body.data.forEach(validateCredential);
|
||||||
|
|
||||||
|
const savedIds = [id1, id2];
|
||||||
|
const returnedIds = response.body.data.map((c) => c.id);
|
||||||
|
|
||||||
|
expect(savedIds).toEqual(returnedIds);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('only own credentials for member', async () => {
|
||||||
|
const role = await testDb.getCredentialOwnerRole();
|
||||||
|
|
||||||
|
const firstMember = member;
|
||||||
|
const secondMember = await testDb.createMember();
|
||||||
|
|
||||||
|
const c1 = await testDb.saveCredential(payload(), { user: firstMember, role });
|
||||||
|
const c2 = await testDb.saveCredential(payload(), { user: secondMember, role });
|
||||||
|
|
||||||
|
const response: GetAllResponse = await testServer
|
||||||
|
.authAgentFor(firstMember)
|
||||||
|
.get('/credentials')
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(response.body.data).toHaveLength(1);
|
||||||
|
|
||||||
|
const [firstMemberCred] = response.body.data;
|
||||||
|
|
||||||
|
validateCredential(firstMemberCred);
|
||||||
|
expect(firstMemberCred.id).toBe(c1.id);
|
||||||
|
expect(firstMemberCred.id).not.toBe(c2.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('filter', () => {
|
||||||
|
test('should filter credentials by field: name - full match', async () => {
|
||||||
|
const role = await testDb.getCredentialOwnerRole();
|
||||||
|
const savedCred = await testDb.saveCredential(payload(), { user: owner, role });
|
||||||
|
|
||||||
|
const response: GetAllResponse = await testServer
|
||||||
|
.authAgentFor(owner)
|
||||||
|
.get('/credentials')
|
||||||
|
.query(`filter={ "name": "${savedCred.name}" }`)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(response.body.data).toHaveLength(1);
|
||||||
|
|
||||||
|
const [returnedCred] = response.body.data;
|
||||||
|
|
||||||
|
expect(returnedCred.name).toBe(savedCred.name);
|
||||||
|
|
||||||
|
const _response = await testServer
|
||||||
|
.authAgentFor(owner)
|
||||||
|
.get('/credentials')
|
||||||
|
.query('filter={ "name": "Non-Existing Credential" }')
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(_response.body.data).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should filter credentials by field: name - partial match', async () => {
|
||||||
|
const role = await testDb.getCredentialOwnerRole();
|
||||||
|
const savedCred = await testDb.saveCredential(payload(), { user: owner, role });
|
||||||
|
|
||||||
|
const partialName = savedCred.name.slice(3);
|
||||||
|
|
||||||
|
const response: GetAllResponse = await testServer
|
||||||
|
.authAgentFor(owner)
|
||||||
|
.get('/credentials')
|
||||||
|
.query(`filter={ "name": "${partialName}" }`)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(response.body.data).toHaveLength(1);
|
||||||
|
|
||||||
|
const [returnedCred] = response.body.data;
|
||||||
|
|
||||||
|
expect(returnedCred.name).toBe(savedCred.name);
|
||||||
|
|
||||||
|
const _response = await testServer
|
||||||
|
.authAgentFor(owner)
|
||||||
|
.get('/credentials')
|
||||||
|
.query('filter={ "name": "Non-Existing Credential" }')
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(_response.body.data).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should filter credentials by field: type - full match', async () => {
|
||||||
|
const role = await testDb.getCredentialOwnerRole();
|
||||||
|
|
||||||
|
const savedCred = await testDb.saveCredential(payload(), { user: owner, role });
|
||||||
|
|
||||||
|
const response: GetAllResponse = await testServer
|
||||||
|
.authAgentFor(owner)
|
||||||
|
.get('/credentials')
|
||||||
|
.query(`filter={ "type": "${savedCred.type}" }`)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(response.body.data).toHaveLength(1);
|
||||||
|
|
||||||
|
const [returnedCred] = response.body.data;
|
||||||
|
|
||||||
|
expect(returnedCred.type).toBe(savedCred.type);
|
||||||
|
|
||||||
|
const _response = await testServer
|
||||||
|
.authAgentFor(owner)
|
||||||
|
.get('/credentials')
|
||||||
|
.query('filter={ "type": "Non-Existing Credential" }')
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(_response.body.data).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should filter credentials by field: type - partial match', async () => {
|
||||||
|
const role = await testDb.getCredentialOwnerRole();
|
||||||
|
|
||||||
|
const savedCred = await testDb.saveCredential(payload(), { user: owner, role });
|
||||||
|
|
||||||
|
const partialType = savedCred.type.slice(3);
|
||||||
|
|
||||||
|
const response: GetAllResponse = await testServer
|
||||||
|
.authAgentFor(owner)
|
||||||
|
.get('/credentials')
|
||||||
|
.query(`filter={ "type": "${partialType}" }`)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(response.body.data).toHaveLength(1);
|
||||||
|
|
||||||
|
const [returnedCred] = response.body.data;
|
||||||
|
|
||||||
|
expect(returnedCred.type).toBe(savedCred.type);
|
||||||
|
|
||||||
|
const _response = await testServer
|
||||||
|
.authAgentFor(owner)
|
||||||
|
.get('/credentials')
|
||||||
|
.query('filter={ "type": "Non-Existing Credential" }')
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(_response.body.data).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('select', () => {
|
||||||
|
test('should select credential field: id', async () => {
|
||||||
|
const role = await testDb.getCredentialOwnerRole();
|
||||||
|
|
||||||
|
await testDb.saveCredential(payload(), { user: owner, role });
|
||||||
|
await testDb.saveCredential(payload(), { user: owner, role });
|
||||||
|
|
||||||
|
const response: GetAllResponse = await testServer
|
||||||
|
.authAgentFor(owner)
|
||||||
|
.get('/credentials')
|
||||||
|
.query('select=["id"]')
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(response.body).toEqual({
|
||||||
|
data: [{ id: any(String) }, { id: any(String) }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should select credential field: name', async () => {
|
||||||
|
const role = await testDb.getCredentialOwnerRole();
|
||||||
|
|
||||||
|
await testDb.saveCredential(payload(), { user: owner, role });
|
||||||
|
await testDb.saveCredential(payload(), { user: owner, role });
|
||||||
|
|
||||||
|
const response: GetAllResponse = await testServer
|
||||||
|
.authAgentFor(owner)
|
||||||
|
.get('/credentials')
|
||||||
|
.query('select=["name"]')
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(response.body).toEqual({
|
||||||
|
data: [{ name: any(String) }, { name: any(String) }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should select credential field: type', async () => {
|
||||||
|
const role = await testDb.getCredentialOwnerRole();
|
||||||
|
|
||||||
|
await testDb.saveCredential(payload(), { user: owner, role });
|
||||||
|
await testDb.saveCredential(payload(), { user: owner, role });
|
||||||
|
|
||||||
|
const response: GetAllResponse = await testServer
|
||||||
|
.authAgentFor(owner)
|
||||||
|
.get('/credentials')
|
||||||
|
.query('select=["type"]')
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(response.body).toEqual({
|
||||||
|
data: [{ type: any(String) }, { type: any(String) }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('take', () => {
|
||||||
|
test('should return n credentials or less, without skip', async () => {
|
||||||
|
const role = await testDb.getCredentialOwnerRole();
|
||||||
|
|
||||||
|
await testDb.saveCredential(payload(), { user: owner, role });
|
||||||
|
await testDb.saveCredential(payload(), { user: owner, role });
|
||||||
|
|
||||||
|
const response = await testServer
|
||||||
|
.authAgentFor(owner)
|
||||||
|
.get('/credentials')
|
||||||
|
.query('take=2')
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(response.body.data).toHaveLength(2);
|
||||||
|
|
||||||
|
response.body.data.forEach(validateCredential);
|
||||||
|
|
||||||
|
const _response = await testServer
|
||||||
|
.authAgentFor(owner)
|
||||||
|
.get('/credentials')
|
||||||
|
.query('take=1')
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(_response.body.data).toHaveLength(1);
|
||||||
|
|
||||||
|
_response.body.data.forEach(validateCredential);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return n credentials or less, with skip', async () => {
|
||||||
|
const role = await testDb.getCredentialOwnerRole();
|
||||||
|
|
||||||
|
await testDb.saveCredential(payload(), { user: owner, role });
|
||||||
|
await testDb.saveCredential(payload(), { user: owner, role });
|
||||||
|
|
||||||
|
const response = await testServer
|
||||||
|
.authAgentFor(owner)
|
||||||
|
.get('/credentials')
|
||||||
|
.query('take=1&skip=1')
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(response.body.data).toHaveLength(1);
|
||||||
|
|
||||||
|
response.body.data.forEach(validateCredential);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function validateCredential(credential: Credentials.WithOwnedByAndSharedWith) {
|
||||||
|
const { name, type, nodesAccess, sharedWith, ownedBy } = credential;
|
||||||
|
|
||||||
|
expect(typeof name).toBe('string');
|
||||||
|
expect(typeof type).toBe('string');
|
||||||
|
expect(typeof nodesAccess[0].nodeType).toBe('string');
|
||||||
|
expect('data' in credential).toBe(false);
|
||||||
|
|
||||||
|
if (sharedWith) expect(Array.isArray(sharedWith)).toBe(true);
|
||||||
|
|
||||||
|
if (ownedBy) {
|
||||||
|
const { id, email, firstName, lastName } = ownedBy;
|
||||||
|
|
||||||
|
expect(typeof id).toBe('string');
|
||||||
|
expect(typeof email).toBe('string');
|
||||||
|
expect(typeof firstName).toBe('string');
|
||||||
|
expect(typeof lastName).toBe('string');
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue