Skip to content

Commit 7c159fd

Browse files
authored
feat(api-service): add internal sdk (#7599)
1 parent fe69629 commit 7c159fd

File tree

462 files changed

+88063
-668
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

462 files changed

+88063
-668
lines changed

.cspell.json

+3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
"ABNF",
77
"ORGANISATION",
88
"EQAs",
9+
"analagous",
910
"addrs",
1011
"subscriberpayloaddto",
1112
"adresses",
13+
"APIJSON",
1214
"sdkerror",
1315
"idempotancy",
1416
"errordto",
@@ -800,6 +802,7 @@
800802
"apps/api/src/app/workflows-v2/maily-test-data.ts",
801803
"apps/api/src/app/workflows-v2/usecases/validate-content/validate-placeholders/validate-placeholder.usecase.ts",
802804
"apps/dashboard/eslint.config.js",
805+
"libs/internal-sdk/**/*",
803806
"apps/web/env.sh",
804807
"apps/web/playwright.config.ts",
805808
"apps/web/src/pages/playground/web-container-configuration/sandbox-vite/*.ts",

.github/actions/run-api/action.yml

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ runs:
1616
with:
1717
targets: build
1818
projects: '@novu/api-service'
19+
args: '--skip-nx-cache'
1920

2021
- name: Start API
2122
shell: bash

.github/workflows/on-pr.yml

+1
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ jobs:
211211
with:
212212
targets: lint,build,test
213213
projects: ${{matrix.projectName}}
214+
args: '--skip-nx-cache'
214215

215216
validate_openapi:
216217
name: Validate OpenAPI

.idea/swagger-settings.xml

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.prettierignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# package.json is formatted by package managers, so we ignore it here
22
package.json
33
apps/api/src/metadata.ts
4+
libs/internal-sdk
45

56
/.nx/cache
67
/.nx/workspace-data

.source

apps/api/e2e/retry.e2e.ts

+18-6
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ describe('Novu Node.js package - Retries and idempotency-key', () => {
3535
},
3636
]);
3737
novuClient = new Novu({
38-
apiKey: 'fakeKey',
38+
security: {
39+
secretKey: 'fakeKey',
40+
},
3941
serverURL: BACKEND_URL,
4042
httpClient: mockHTTPClient,
4143
});
@@ -62,7 +64,9 @@ describe('Novu Node.js package - Retries and idempotency-key', () => {
6264
},
6365
]);
6466
novuClient = new Novu({
65-
apiKey: 'fakeKey',
67+
security: {
68+
secretKey: 'fakeKey',
69+
},
6670
serverURL: BACKEND_URL,
6771
httpClient,
6872
});
@@ -93,7 +97,9 @@ describe('Novu Node.js package - Retries and idempotency-key', () => {
9397
},
9498
]);
9599
novuClient = new Novu({
96-
apiKey: 'fakeKey',
100+
security: {
101+
secretKey: 'fakeKey',
102+
},
97103
serverURL: BACKEND_URL,
98104
httpClient: mockHTTPClient,
99105
});
@@ -106,7 +112,9 @@ describe('Novu Node.js package - Retries and idempotency-key', () => {
106112

107113
it('should fail after reaching max retries', async () => {
108114
novuClient = new Novu({
109-
apiKey: 'fakeKey',
115+
security: {
116+
secretKey: 'fakeKey',
117+
},
110118
serverURL: BACKEND_URL,
111119
httpClient: new MockHTTPClient([
112120
{
@@ -161,7 +169,9 @@ describe('Novu Node.js package - Retries and idempotency-key', () => {
161169
NON_RECOVERABLE_ERRORS.forEach(([status, message]) => {
162170
it('should not retry on non-recoverable %i error', async () => {
163171
novuClient = new Novu({
164-
apiKey: 'fakeKey',
172+
security: {
173+
secretKey: 'fakeKey',
174+
},
165175
serverURL: BACKEND_URL,
166176
httpClient: new MockHTTPClient([
167177
{
@@ -234,7 +244,9 @@ describe('Novu Node.js package - Retries and idempotency-key', () => {
234244
]);
235245

236246
novuClient = new Novu({
237-
apiKey: 'fakeKey',
247+
security: {
248+
secretKey: 'fakeKey',
249+
},
238250
serverURL: BACKEND_URL,
239251
httpClient: mockClient,
240252
});

apps/api/e2e/setup.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { testServer } from '@novu/testing';
22
import sinon from 'sinon';
33
import chai from 'chai';
4-
import { connection, default as mongoose } from 'mongoose';
4+
import { default as mongoose } from 'mongoose';
55
import { bootstrap } from '../src/bootstrap';
66

77
async function dropDatabase() {
@@ -21,7 +21,7 @@ before(async () => {
2121
*/
2222
chai.config.truncateThreshold = 0;
2323
await dropDatabase();
24-
await testServer.create(await bootstrap());
24+
await testServer.create((await bootstrap()).app);
2525
});
2626

2727
after(async () => {

apps/api/exportOpenAPIJSON.ts

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { writeFileSync } from 'fs';
2+
import { bootstrap } from './src/bootstrap'; // Adjust the path according to your project structure
3+
4+
async function exportOpenAPIJSON() {
5+
try {
6+
const { app, document } = await bootstrap();
7+
8+
// Write the Swagger document to a file
9+
writeFileSync('./dist/swagger-spec.json', JSON.stringify(document, null, 2));
10+
console.log('Swagger document generated at swagger-spec.json');
11+
12+
// Close the application
13+
await app.close();
14+
console.log('App Closed');
15+
} catch (error) {
16+
console.error('Error generating Swagger document:', error);
17+
} finally {
18+
// Ensure the process exits
19+
process.exit();
20+
}
21+
}
22+
23+
exportOpenAPIJSON();

apps/api/package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"scripts": {
99
"prebuild": "rimraf dist",
1010
"build": "pnpm build:metadata && nest build",
11+
"build:generate": "pnpm build:metadata && nest build && pnpm generate:swagger && pnpm generate:sdk",
1112
"build:watch": "pnpm build:metadata && nest build --watch",
1213
"format": "prettier --write \"src/**/*.ts\"",
1314
"docker:build": "pnpm --silent --workspace-root pnpm-context -- apps/api/Dockerfile | BULL_MQ_PRO_NPM_TOKEN=${BULL_MQ_PRO_NPM_TOKEN} docker buildx build --load -t novu-api --secret id=BULL_MQ_PRO_NPM_TOKEN --build-arg PACKAGE_PATH=apps/api - $DOCKER_BUILD_ARGUMENTS",
@@ -22,6 +23,8 @@
2223
"lint:fix": "pnpm lint -- --fix",
2324
"lint:openapi": "spectral lint http://127.0.0.1:${PORT:-3000}/openapi.yaml",
2425
"pretest": "pnpm build:metadata",
26+
"generate:swagger": "cross-env NODE_ENV=test PORT=1336 ts-node exportOpenAPIJSON.ts",
27+
"generate:sdk": " (cd ../../libs/internal-sdk && speakeasy run --skip-compile --minimal --skip-versioning) && (cd ../../libs/internal-sdk && pnpm build) ",
2528
"test": "cross-env TS_NODE_COMPILER_OPTIONS='{\"strictNullChecks\": false}' NODE_ENV=test mocha --require ts-node/register --exit 'src/**/*.spec.ts'",
2629
"test:e2e:novu-v1": "cross-env TS_NODE_COMPILER_OPTIONS='{\"strictNullChecks\": false}' NODE_ENV=test mocha --grep '#novu-v1' --require ts-node/register --exit --file e2e/setup.ts src/**/*.e2e{,-ee}.ts",
2730
"test:e2e:novu-v2": "cross-env TS_NODE_COMPILER_OPTIONS='{\"strictNullChecks\": false}' NODE_ENV=test CI_EE_TEST=true CLERK_ENABLED=true NODE_OPTIONS=--max_old_space_size=8192 mocha --grep '#novu-v2' --require ts-node/register --exit --file e2e/setup.ts 'src/**/*.e2e{,-ee}.ts'",
@@ -44,7 +47,7 @@
4447
"@nestjs/swagger": "7.4.0",
4548
"@nestjs/terminus": "10.2.3",
4649
"@nestjs/throttler": "6.2.1",
47-
"@novu/api": "0.1.0",
50+
"@novu/api": "workspace:*",
4851
"@novu/application-generic": "workspace:*",
4952
"@novu/dal": "workspace:*",
5053
"@novu/framework": "workspace:*",

apps/api/src/app.module.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { InvitesModule } from './app/invites/invites.module';
2727
import { ContentTemplatesModule } from './app/content-templates/content-templates.module';
2828
import { IntegrationModule } from './app/integrations/integrations.module';
2929
import { ChangeModule } from './app/change/change.module';
30-
import { SubscribersModule } from './app/subscribers/subscribers.module';
30+
import { SubscribersV1Module } from './app/subscribers/subscribersV1.module';
3131
import { FeedsModule } from './app/feeds/feeds.module';
3232
import { LayoutsModule } from './app/layouts/layouts.module';
3333
import { MessagesModule } from './app/messages/messages.module';
@@ -49,7 +49,7 @@ import { WorkflowModule } from './app/workflows-v2/workflow.module';
4949
import { WorkflowModuleV1 } from './app/workflows-v1/workflow-v1.module';
5050
import { EnvironmentsModuleV1 } from './app/environments-v1/environments-v1.module';
5151
import { EnvironmentsModule } from './app/environments-v2/environments.module';
52-
import { SubscriberModule } from './app/subscribers-v2/subscriber.module';
52+
import { SubscribersModule } from './app/subscribers-v2/subscribers.module';
5353

5454
const enterpriseImports = (): Array<Type | DynamicModule | Promise<DynamicModule> | ForwardReference> => {
5555
const modules: Array<Type | DynamicModule | Promise<DynamicModule> | ForwardReference> = [];
@@ -97,8 +97,8 @@ const baseModules: Array<Type | DynamicModule | Promise<DynamicModule> | Forward
9797
UserModule,
9898
IntegrationModule,
9999
ChangeModule,
100+
SubscribersV1Module,
100101
SubscribersModule,
101-
SubscriberModule,
102102
FeedsModule,
103103
LayoutsModule,
104104
MessagesModule,

apps/api/src/app/auth/usecases/password-reset-request/password-reset-request.usecase.ts

-15
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { IUserResetTokenCount, UserEntity, UserRepository } from '@novu/dal';
55
import { buildUserKey, InvalidateCacheService } from '@novu/application-generic';
66

77
import { normalizeEmail, PasswordResetFlowEnum } from '@novu/shared';
8-
import { Novu } from '@novu/api';
98
import { PasswordResetRequestCommand } from './password-reset-request.command';
109

1110
@Injectable()
@@ -39,21 +38,7 @@ export class PasswordResetRequest {
3938
await this.userRepository.updatePasswordResetToken(foundUser._id, token, resetTokenCount);
4039

4140
if ((process.env.NODE_ENV === 'dev' || process.env.NODE_ENV === 'production') && process.env.NOVU_API_KEY) {
42-
const novu = new Novu({ apiKey: process.env.NOVU_API_KEY });
4341
const resetPasswordLink = PasswordResetRequest.getResetRedirectLink(token, foundUser, command.src);
44-
45-
await novu.trigger({
46-
name: process.env.NOVU_TEMPLATEID_PASSWORD_RESET || 'password-reset-llS-wzWMq',
47-
to: [
48-
{
49-
subscriberId: foundUser._id,
50-
email: foundUser.email,
51-
},
52-
],
53-
payload: {
54-
resetPasswordLink,
55-
},
56-
});
5742
}
5843
}
5944

apps/api/src/app/events/dtos/trigger-event-request.dto.ts

+2
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ export class TriggerEventRequestDto {
152152
{ type: 'string', description: 'Unique identifier of a subscriber in your systems' },
153153
{ $ref: getSchemaPath(SubscriberPayloadDto) },
154154
],
155+
required: false,
155156
})
156157
@IsOptional()
157158
@ValidateIf((_, value) => typeof value !== 'string')
@@ -166,6 +167,7 @@ export class TriggerEventRequestDto {
166167
{ type: 'string', description: 'Unique identifier of a tenant in your system' },
167168
{ $ref: getSchemaPath(TenantPayloadDto) },
168169
],
170+
required: false,
169171
})
170172
@IsOptional()
171173
@ValidateIf((_, value) => typeof value !== 'string')

apps/api/src/app/events/events.module.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ import { USE_CASES } from './usecases';
1313
import { SharedModule } from '../shared/shared.module';
1414
import { WidgetsModule } from '../widgets/widgets.module';
1515
import { AuthModule } from '../auth/auth.module';
16-
import { SubscribersModule } from '../subscribers/subscribers.module';
1716
import { ContentTemplatesModule } from '../content-templates/content-templates.module';
1817
import { IntegrationModule } from '../integrations/integrations.module';
1918
import { ExecutionDetailsModule } from '../execution-details/execution-details.module';
2019
import { TopicsModule } from '../topics/topics.module';
2120
import { LayoutsModule } from '../layouts/layouts.module';
2221
import { TenantModule } from '../tenant/tenant.module';
2322
import { BridgeModule } from '../bridge';
23+
import { SubscribersV1Module } from '../subscribers/subscribersV1.module';
2424

2525
const PROVIDERS = [GetNovuProviderCredentials, StorageHelperService, EventsDistributedLockService];
2626

@@ -30,7 +30,7 @@ const PROVIDERS = [GetNovuProviderCredentials, StorageHelperService, EventsDistr
3030
TerminusModule,
3131
WidgetsModule,
3232
AuthModule,
33-
SubscribersModule,
33+
SubscribersV1Module,
3434
ContentTemplatesModule,
3535
IntegrationModule,
3636
ExecutionDetailsModule,

apps/api/src/app/inbox/inbox.module.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import { Module } from '@nestjs/common';
22
import { AuthModule } from '../auth/auth.module';
33
import { IntegrationModule } from '../integrations/integrations.module';
44
import { SharedModule } from '../shared/shared.module';
5-
import { SubscribersModule } from '../subscribers/subscribers.module';
5+
import { SubscribersV1Module } from '../subscribers/subscribersV1.module';
66
import { InboxController } from './inbox.controller';
77
import { USE_CASES } from './usecases';
88
import { PreferencesModule } from '../preferences';
99

1010
@Module({
11-
imports: [SharedModule, SubscribersModule, AuthModule, IntegrationModule, PreferencesModule],
11+
imports: [SharedModule, SubscribersV1Module, AuthModule, IntegrationModule, PreferencesModule],
1212
providers: [...USE_CASES],
1313
exports: [...USE_CASES],
1414
controllers: [InboxController],

apps/api/src/app/invites/usecases/accept-invite/accept-invite.usecase.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export class AcceptInvite {
5656

5757
try {
5858
if ((process.env.NODE_ENV === 'dev' || process.env.NODE_ENV === 'production') && process.env.NOVU_API_KEY) {
59-
const novu = new Novu({ apiKey: process.env.NOVU_API_KEY });
59+
const novu = new Novu({ security: { secretKey: process.env.NOVU_API_KEY } });
6060

6161
await novu.trigger({
6262
name: process.env.NOVU_TEMPLATEID_INVITE_ACCEPTED || 'invite-accepted-dEQAsKD1E',

apps/api/src/app/invites/usecases/invite-member/invite-member.usecase.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export class InviteMember {
3333
const token = createGuid();
3434

3535
if (process.env.NOVU_API_KEY && (process.env.NODE_ENV === 'dev' || process.env.NODE_ENV === 'production')) {
36-
const novu = new Novu({ apiKey: process.env.NOVU_API_KEY });
36+
const novu = new Novu({ security: { secretKey: process.env.NOVU_API_KEY } });
3737
await novu.trigger({
3838
name: process.env.NOVU_TEMPLATEID_INVITE_TO_ORGANISATION || 'invite-to-organization-wBnO8NpDn',
3939
to: [

apps/api/src/app/invites/usecases/resend-invite/resend-invite.usecase.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export class ResendInvite {
3434
const token = createGuid();
3535

3636
if (process.env.NODE_ENV === 'dev' || process.env.NODE_ENV === 'production') {
37-
const novu = new Novu({ apiKey: process.env.NOVU_API_KEY ?? '' });
37+
const novu = new Novu({ security: { secretKey: process.env.NOVU_API_KEY } });
3838

3939
// cspell:disable-next
4040
await novu.trigger({

apps/api/src/app/messages/messages.module.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import { Module, forwardRef } from '@nestjs/common';
1+
import { forwardRef, Module } from '@nestjs/common';
22
import { TerminusModule } from '@nestjs/terminus';
33
import { SharedModule } from '../shared/shared.module';
44
import { USE_CASES } from './usecases';
55
import { MessagesController } from './messages.controller';
66
import { AuthModule } from '../auth/auth.module';
77
import { WidgetsModule } from '../widgets/widgets.module';
8-
import { SubscribersModule } from '../subscribers/subscribers.module';
8+
import { SubscribersV1Module } from '../subscribers/subscribersV1.module';
99

1010
@Module({
11-
imports: [SharedModule, SubscribersModule, AuthModule, TerminusModule, forwardRef(() => WidgetsModule)],
11+
imports: [SharedModule, SubscribersV1Module, AuthModule, TerminusModule, forwardRef(() => WidgetsModule)],
1212
controllers: [MessagesController],
1313
providers: [...USE_CASES],
1414
exports: [...USE_CASES],

apps/api/src/app/organization/dtos/update-branding-details.dto.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { IsHexColor, IsOptional, IsString, IsUrl } from 'class-validator';
22
import { IsImageUrl } from '../../shared/validators/image.validator';
33

44
const environments = ['production', 'test'];
5-
const protocols = environments.includes(process.env.NODE_ENV) ? ['https'] : ['http', 'https'];
5+
const protocols = environments.includes(process.env.NODE_ENV || '') ? ['https'] : ['http', 'https'];
66

77
export class UpdateBrandingDetailsDto {
88
@IsUrl({

0 commit comments

Comments
 (0)