Skip to content

Commit

Permalink
Merge pull request #115 from conceptadev/feature/add-org-intivitation
Browse files Browse the repository at this point in the history
Feature/add org intivitation
  • Loading branch information
MrMaz authored Oct 19, 2023
2 parents f62778f + a6bc5a4 commit 18d95a3
Show file tree
Hide file tree
Showing 52 changed files with 741 additions and 69 deletions.
2 changes: 1 addition & 1 deletion packages/nestjs-invitation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"@concepta/nestjs-typeorm-ext": "^4.0.0-alpha.29",
"@concepta/nestjs-user": "^4.0.0-alpha.29",
"@concepta/typeorm-common": "^4.0.0-alpha.29",
"@concepta/typeorm-seeding": "^4.0.0-alpha.3",
"@concepta/typeorm-seeding": "^4.0.0-beta.1",
"@faker-js/faker": "6.0.0-alpha.6",
"@nestjs-modules/mailer": "^1.6.1",
"@nestjs/testing": "^9.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { MailerModule, MailerService } from '@nestjs-modules/mailer';

import { InvitationModule } from '../invitation.module';
import { InvitationGetUserEventAsync } from '../events/invitation-get-user.event';

import { InvitationEntityFixture } from './invitation/entities/invitation.entity.fixture';
import { InvitationAcceptedEventAsync } from '../events/invitation-accepted.event';
import { UserOtpEntityFixture } from './user/entities/user-otp-entity.fixture';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { INestApplication } from '@nestjs/common';
import { UserFactory } from '@concepta/nestjs-user/src/seeding';
import { ConfigService, ConfigType } from '@nestjs/config';
import { OtpService } from '@concepta/nestjs-otp';
import { OtpInterface, UserInterface } from '@concepta/ts-common';
import {
INVITATION_MODULE_CATEGORY_ORG_KEY,
INVITATION_MODULE_CATEGORY_USER_KEY,
OtpInterface,
UserInterface,
} from '@concepta/ts-common';
import { EmailService } from '@concepta/nestjs-email';
import { SeedingSource } from '@concepta/typeorm-seeding';
import { getDataSourceToken } from '@nestjs/typeorm';
Expand All @@ -17,19 +22,19 @@ import { invitationDefaultConfig } from '../config/invitation-default.config';
import { InvitationFactory } from '../invitation.factory';
import { InvitationEntityInterface } from '../interfaces/invitation.entity.interface';
import { InvitationSettingsInterface } from '../interfaces/invitation-settings.interface';

import { AppModuleFixture } from '../__fixtures__/app.module.fixture';
import { InvitationEntityFixture } from '../__fixtures__/invitation/entities/invitation.entity.fixture';
import { UserEntityFixture } from '../__fixtures__/user/entities/user-entity.fixture';

describe('InvitationController (e2e)', () => {
const category = 'invitation';
const userCategory = INVITATION_MODULE_CATEGORY_USER_KEY;
const orgCategory = INVITATION_MODULE_CATEGORY_ORG_KEY;
const payload = { moreData: 'foo' };

let app: INestApplication;
let invitationFactory: InvitationFactory;
let seedingSource: SeedingSource;
let user: UserEntityFixture;
let invitation: InvitationEntityInterface;
let otpService: OtpService;
let configService: ConfigService;
let config: ConfigType<typeof invitationDefaultConfig>;
Expand Down Expand Up @@ -62,33 +67,84 @@ describe('InvitationController (e2e)', () => {
seedingSource,
});

const invitationFactory = new InvitationFactory({
invitationFactory = new InvitationFactory({
entity: InvitationEntityFixture,
seedingSource,
});

user = await userFactory.create();
invitation = await invitationFactory.create({ category, user });
});

afterEach(async () => {
jest.clearAllMocks();
return app ? await app.close() : undefined;
});

describe('Type: org', () => {
let invitation: InvitationEntityInterface;

beforeEach(async () => {
invitation = await invitationFactory.create({
category: orgCategory,
user,
});
});

it('POST invitation', async () => {
await createInvite(app, {
email: user.email,
category: orgCategory,
payload,
});
});

it('PATCH invitation-acceptance', async () => {
const { code } = invitation;

const otp = await createOtp(config, otpService, user, orgCategory);

const { passcode } = otp;

await supertest(app.getHttpServer())
.patch(`/invitation-acceptance/${code}`)
.send({
passcode,
payload: { newPassword: 'hOdv2A2h%' },
} as InvitationAcceptInviteDto)
.expect(200);
});
});

describe('Type: user', () => {
let invitation: InvitationEntityInterface;

beforeEach(async () => {
invitation = await invitationFactory.create({
category: userCategory,
user,
});
});

it('POST invitation', async () => {
await createInvite({ email: user.email, category, payload });
await createInvite(app, {
email: user.email,
category: userCategory,
payload,
});
});

it('POST invitation (create new user)', async () => {
await createInvite({ email: '[email protected]', category, payload });
await createInvite(app, {
email: '[email protected]',
category: userCategory,
payload,
});
});

it('POST invitation reattempt', async () => {
const invitationDto = await createInvite({
const invitationDto = await createInvite(app, {
email: '[email protected]',
category,
category: userCategory,
payload,
});

Expand All @@ -100,7 +156,7 @@ describe('InvitationController (e2e)', () => {
it('PATCH invitation-acceptance', async () => {
const { code } = invitation;

const otp = await createOtp(config, otpService, user, category);
const otp = await createOtp(config, otpService, user, userCategory);

const { passcode } = otp;

Expand All @@ -116,7 +172,7 @@ describe('InvitationController (e2e)', () => {
it('GET invitation-acceptance', async () => {
const { code } = invitation;

const otp = await createOtp(config, otpService, user, category);
const otp = await createOtp(config, otpService, user, userCategory);

const { passcode } = otp;

Expand All @@ -128,12 +184,12 @@ describe('InvitationController (e2e)', () => {
it('GET invitation', async () => {
const invitationCreateDto = {
email: user.email,
category,
category: userCategory,
payload,
} as InvitationCreateDto;
const invite1 = await createInvite(invitationCreateDto);
const invite2 = await createInvite(invitationCreateDto);
const invite3 = await createInvite(invitationCreateDto);
const invite1 = await createInvite(app, invitationCreateDto);
const invite2 = await createInvite(app, invitationCreateDto);
const invite3 = await createInvite(app, invitationCreateDto);

const response = await supertest(app.getHttpServer())
.get(`/invitation?s={"email": "${invitationCreateDto.email}"}`)
Expand All @@ -149,9 +205,9 @@ describe('InvitationController (e2e)', () => {
});

it('GET invitation/:id', async () => {
const invitation = await createInvite({
const invitation = await createInvite(app, {
email: user.email,
category,
category: userCategory,
payload,
});

Expand All @@ -165,9 +221,9 @@ describe('InvitationController (e2e)', () => {
});

it('DELETE invitation/:id', async () => {
const invitation = await createInvite({
const invitation = await createInvite(app, {
email: user.email,
category,
category: userCategory,
payload,
});

Expand All @@ -179,20 +235,21 @@ describe('InvitationController (e2e)', () => {
.get(`/invitation/${invitation.id}`)
.expect(404);
});

const createInvite = async (
invitationCreateDto: InvitationCreateDto,
): Promise<InvitationDto> => {
const response = await supertest(app.getHttpServer())
.post('/invitation')
.send(invitationCreateDto)
.expect(201);

return response.body as InvitationDto;
};
});
});

const createInvite = async (
app: INestApplication,
invitationCreateDto: InvitationCreateDto,
): Promise<InvitationDto> => {
const response = await supertest(app.getHttpServer())
.post('/invitation')
.send(invitationCreateDto)
.expect(201);

return response.body as InvitationDto;
};

const createOtp = async (
config: ConfigType<typeof invitationDefaultConfig>,
otpService: OtpService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export class InvitationController
email,
category,
code: randomUUID(),
constraints: payload,
});

if (user !== undefined && invite !== undefined) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { AuditPostgresEmbed } from '@concepta/typeorm-common';

import { InvitationEntityInterface } from '../interfaces/invitation.entity.interface';
import { LiteralObject } from '@nestjs/common';

// TODO check this entity later
export abstract class InvitationPostgresEntity
Expand All @@ -31,5 +32,8 @@ export abstract class InvitationPostgresEntity
@Column()
category!: string;

@Column({ type: 'jsonb' })
constraints?: LiteralObject;

user!: ReferenceIdInterface;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { AuditSqlLiteEmbed } from '@concepta/typeorm-common';

import { InvitationEntityInterface } from '../interfaces/invitation.entity.interface';
import { LiteralObject } from '@nestjs/common';

// TODO check this entity later
export abstract class InvitationSqliteEntity
Expand All @@ -31,5 +32,8 @@ export abstract class InvitationSqliteEntity
@Column()
category!: string;

@Column({ type: 'simple-json', nullable: true })
constraints?: LiteralObject;

user!: ReferenceIdInterface;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import { getDataSourceToken } from '@nestjs/typeorm';
import { OtpInterface, UserInterface } from '@concepta/ts-common';
import {
INVITATION_MODULE_CATEGORY_USER_KEY,
OtpInterface,
UserInterface,
} from '@concepta/ts-common';
import { UserEntityInterface } from '@concepta/nestjs-user';
import { OtpService } from '@concepta/nestjs-otp';
import { UserFactory } from '@concepta/nestjs-user/src/seeding';
Expand All @@ -10,18 +14,16 @@ import { EmailService } from '@concepta/nestjs-email';
import { EventDispatchService } from '@concepta/nestjs-event';

import { INVITATION_MODULE_SETTINGS_TOKEN } from '../invitation.constants';

import { InvitationFactory } from '../invitation.factory';
import { InvitationSettingsInterface } from '../interfaces/invitation-settings.interface';
import { InvitationEntityInterface } from '../interfaces/invitation.entity.interface';
import { InvitationAcceptanceService } from './invitation-acceptance.service';

import { AppModuleFixture } from '../__fixtures__/app.module.fixture';
import { InvitationEntityFixture } from '../__fixtures__/invitation/entities/invitation.entity.fixture';
import { UserEntityFixture } from '../__fixtures__/user/entities/user-entity.fixture';

describe(InvitationAcceptanceService, () => {
const category = 'invitation';
const category = INVITATION_MODULE_CATEGORY_USER_KEY;

let spyEmailService: jest.SpyInstance;
let spyEventDispatchService: jest.SpyInstance;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export class InvitationAcceptanceService extends BaseService<InvitationEntityInt
queryOptions?: QueryOptionsInterface,
): Promise<boolean> {
const invitationAcceptedEventAsync = new InvitationAcceptedEventAsync({
...invitationDto,
invitation: invitationDto,
data: payload,
queryOptions,
});
Expand All @@ -109,7 +109,7 @@ export class InvitationAcceptanceService extends BaseService<InvitationEntityInt
invitationAcceptedEventAsync,
);

return eventResult.some((it) => it === true);
return eventResult.every((it) => it === true);
}

async sendEmail(email: string): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,25 @@ import { Repository } from 'typeorm';
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import { getDataSourceToken } from '@nestjs/typeorm';

import { UserEntityInterface } from '@concepta/nestjs-user';
import { OtpService } from '@concepta/nestjs-otp';
import { EmailService } from '@concepta/nestjs-email';
import { UserFactory } from '@concepta/nestjs-user/src/seeding';
import { SeedingSource } from '@concepta/typeorm-seeding';
import { getDynamicRepositoryToken } from '@concepta/nestjs-typeorm-ext';
import { INVITATION_MODULE_CATEGORY_USER_KEY } from '@concepta/ts-common';

import { INVITATION_MODULE_INVITATION_ENTITY_KEY } from '../invitation.constants';

import { InvitationFactory } from '../invitation.factory';
import { InvitationSendService } from './invitation-send.service';
import { InvitationRevocationService } from './invitation-revocation.service';
import { InvitationEntityInterface } from '../interfaces/invitation.entity.interface';

import { AppModuleFixture } from '../__fixtures__/app.module.fixture';
import { InvitationEntityFixture } from '../__fixtures__/invitation/entities/invitation.entity.fixture';
import { UserEntityFixture } from '../__fixtures__/user/entities/user-entity.fixture';

describe(InvitationRevocationService, () => {
const category = 'invitation';
const category = INVITATION_MODULE_CATEGORY_USER_KEY;

let spyEmailService: jest.SpyInstance;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@ import { Repository } from 'typeorm';
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import { getDataSourceToken } from '@nestjs/typeorm';

import { UserEntityInterface } from '@concepta/nestjs-user';
import { EmailService } from '@concepta/nestjs-email';
import { getDynamicRepositoryToken } from '@concepta/nestjs-typeorm-ext';
import { SeedingSource } from '@concepta/typeorm-seeding';
import { UserFactory } from '@concepta/nestjs-user/src/seeding';
import { INVITATION_MODULE_CATEGORY_USER_KEY } from '@concepta/ts-common';

import { INVITATION_MODULE_SETTINGS_TOKEN } from '../invitation.constants';
import { InvitationSendService } from './invitation-send.service';
import { InvitationSettingsInterface } from '../interfaces/invitation-settings.interface';

import { AppModuleFixture } from '../__fixtures__/app.module.fixture';
import { UserEntityFixture } from '../__fixtures__/user/entities/user-entity.fixture';
import { UserOtpEntityFixture } from '../__fixtures__/user/entities/user-otp-entity.fixture';
Expand Down Expand Up @@ -75,14 +74,18 @@ describe(InvitationSendService, () => {
it('Should send invitation email', async () => {
const inviteCode = randomUUID();

await invitationSendService.send(testUser, inviteCode, 'invitation');
await invitationSendService.send(
testUser,
inviteCode,
INVITATION_MODULE_CATEGORY_USER_KEY,
);

const otps = await userOtpRepo.find({
where: { assignee: { id: testUser.id } },
});

expect(otps.length).toEqual(1);
expect(otps[0].category).toEqual('invitation');
expect(otps[0].category).toEqual(INVITATION_MODULE_CATEGORY_USER_KEY);
expect(spyEmailService).toHaveBeenCalledTimes(1);

const { passcode, expirationDate } = otps[0];
Expand Down
Loading

0 comments on commit 18d95a3

Please sign in to comment.