Skip to content

Commit

Permalink
fix(shared): Remove all dependencies from @novu/shared
Browse files Browse the repository at this point in the history
Prevent accidental dependency leaks, especially in client side libraries.
  • Loading branch information
SokratisVidros committed Nov 7, 2024
1 parent 0385564 commit c379f2e
Show file tree
Hide file tree
Showing 29 changed files with 199 additions and 186 deletions.
4 changes: 2 additions & 2 deletions apps/api/src/app/auth/services/passport/apikey.strategy.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { HeaderAPIKeyStrategy } from 'passport-headerapikey';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { AuthService } from '@novu/application-generic';
import { ApiAuthSchemeEnum, UserSessionData, HttpRequestHeaderKeysEnum } from '@novu/shared';
import { AuthService, HttpRequestHeaderKeysEnum } from '@novu/application-generic';
import { ApiAuthSchemeEnum, UserSessionData } from '@novu/shared';

@Injectable()
export class ApiKeyStrategy extends PassportStrategy(HeaderAPIKeyStrategy) {
Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/app/auth/services/passport/jwt.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import type http from 'http';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { ApiAuthSchemeEnum, HttpRequestHeaderKeysEnum, UserSessionData } from '@novu/shared';
import { AuthService, Instrument } from '@novu/application-generic';
import { AuthService, Instrument, HttpRequestHeaderKeysEnum } from '@novu/application-generic';
import { ApiAuthSchemeEnum, UserSessionData } from '@novu/shared';
import { EnvironmentRepository } from '@novu/dal';

@Injectable()
Expand Down
10 changes: 3 additions & 7 deletions apps/api/src/app/rate-limiting/guards/throttler.guard.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { UserSession } from '@novu/testing';
import { ApiRateLimitCategoryEnum, ApiRateLimitCostEnum, ApiServiceLevelEnum } from '@novu/shared';
import { expect } from 'chai';
import {
ApiRateLimitCategoryEnum,
ApiRateLimitCostEnum,
ApiServiceLevelEnum,
HttpResponseHeaderKeysEnum,
} from '@novu/shared';
import { HttpResponseHeaderKeysEnum } from '@novu/application-generic';
import { UserSession } from '@novu/testing';

const mockSingleCost = 1;
const mockBulkCost = 5;
Expand Down
11 changes: 7 additions & 4 deletions apps/api/src/app/rate-limiting/guards/throttler.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@ import {
ThrottlerException,
ThrottlerGuard,
ThrottlerModuleOptions,
ThrottlerOptions,
ThrottlerRequest,
ThrottlerStorage,
} from '@nestjs/throttler';
import { CallHandler, ExecutionContext, Injectable, Logger, NestInterceptor } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { GetFeatureFlag, GetFeatureFlagCommand, Instrument } from '@novu/application-generic';
import {
GetFeatureFlag,
GetFeatureFlagCommand,
Instrument,
HttpRequestHeaderKeysEnum,
HttpResponseHeaderKeysEnum,
} from '@novu/application-generic';
import {
ApiAuthSchemeEnum,
ApiRateLimitCategoryEnum,
ApiRateLimitCostEnum,
HttpRequestHeaderKeysEnum,
HttpResponseHeaderKeysEnum,
FeatureFlagsKeysEnum,
UserSessionData,
} from '@novu/shared';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HeaderObject, HttpResponseHeaderKeysEnum } from '@novu/shared';
import { HeaderObject, HttpResponseHeaderKeysEnum } from '@novu/application-generic';

export const COMMON_RESPONSE_HEADERS: Array<HttpResponseHeaderKeysEnum> = [
HttpResponseHeaderKeysEnum.CONTENT_TYPE,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiResponseOptions } from '@nestjs/swagger';
import { ApiResponseDecoratorName, HttpResponseHeaderKeysEnum } from '@novu/shared';
import { ApiResponseDecoratorName, HttpResponseHeaderKeysEnum } from '@novu/application-generic';
import { THROTTLED_EXCEPTION_MESSAGE } from '../../../rate-limiting/guards';
import { createReusableHeaders } from '../swagger';

Expand Down
3 changes: 1 addition & 2 deletions apps/api/src/app/shared/framework/idempotency.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { UserSession } from '@novu/testing';
import { HttpResponseHeaderKeysEnum } from '@novu/shared';
import { CacheService } from '@novu/application-generic';
import { CacheService, HttpResponseHeaderKeysEnum } from '@novu/application-generic';
import { expect } from 'chai';
import { DOCS_LINK } from './idempotency.interceptor';

Expand Down
10 changes: 8 additions & 2 deletions apps/api/src/app/shared/framework/idempotency.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@ import {
ServiceUnavailableException,
UnprocessableEntityException,
} from '@nestjs/common';
import { CacheService, GetFeatureFlag, GetFeatureFlagCommand, Instrument } from '@novu/application-generic';
import {
CacheService,
GetFeatureFlag,
GetFeatureFlagCommand,
Instrument,
HttpResponseHeaderKeysEnum,
} from '@novu/application-generic';
import { Observable, of, throwError } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { createHash } from 'crypto';
import { ApiAuthSchemeEnum, HttpResponseHeaderKeysEnum, FeatureFlagsKeysEnum, UserSessionData } from '@novu/shared';
import { ApiAuthSchemeEnum, FeatureFlagsKeysEnum, UserSessionData } from '@novu/shared';

const LOG_CONTEXT = 'IdempotencyInterceptor';
const IDEMPOTENCY_CACHE_TTL = 60 * 60 * 24; // 24h
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HeaderObjects, HttpResponseHeaderKeysEnum } from '@novu/shared';
import { HeaderObjects, HttpResponseHeaderKeysEnum } from '@novu/application-generic';
import { OpenAPIObject } from '@nestjs/swagger';
import { RESPONSE_HEADER_CONFIG } from '../constants/headers.schema';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { applyDecorators } from '@nestjs/common';
// eslint-disable-next-line import/no-namespace
import * as nestSwagger from '@nestjs/swagger';
import { ApiResponseOptions } from '@nestjs/swagger';
import type { ApiResponseDecoratorName } from '@novu/shared';
import type { ApiResponseDecoratorName } from '@novu/application-generic';
import { COMMON_RESPONSE_HEADERS, COMMON_RESPONSES } from '../constants';
import { createReusableHeaders } from './headers.decorator';

Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/config/cors.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { INestApplication, Logger } from '@nestjs/common';
import { HttpRequestHeaderKeysEnum } from '@novu/shared';
import { HttpRequestHeaderKeysEnum } from '@novu/application-generic';

export const corsOptionsDelegate: Parameters<INestApplication['enableCors']>[0] = function (req: Request, callback) {
const corsOptions: Parameters<typeof callback>[1] = {
Expand Down
3 changes: 2 additions & 1 deletion libs/application-generic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@
"sinon": "^9.2.4",
"ts-jest": "^27.0.5",
"ts-node": "~10.9.1",
"typescript": "5.6.2"
"typescript": "5.6.2",
"vitest": "^2.0.5"
},
"files": [
"build/main",
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
/* cSpell:enableCompoundWords */
import { expect, describe, it } from 'vitest';
import { WithRequired, ConvertToConstantCase, testHttpHeaderEnumValidity, ValidateHttpHeaderCase } from './utils.types';
import {
WithRequired,
ConvertToConstantCase,
testHttpHeaderEnumValidity,
ValidateHttpHeaderCase,
} from './utils.types';

describe('HTTP headers', () => {
/**
Expand Down Expand Up @@ -34,40 +39,56 @@ export const invalidTestType: WithRequired<TestWithRequired, 'optional'> = {
* ConvertToConstantCase tests
*/
// Valid
export const validConstantSingleString: ConvertToConstantCase<'Single'> = 'SINGLE';
export const validConstantSingleSingleString: ConvertToConstantCase<'Double-String'> = 'DOUBLE_STRING';
export const validConstantDoubleSingleString: ConvertToConstantCase<'DoubleWord-String'> = 'DOUBLEWORD_STRING';
export const validConstantSingleString: ConvertToConstantCase<'Single'> =
'SINGLE';
export const validConstantSingleSingleString: ConvertToConstantCase<'Double-String'> =
'DOUBLE_STRING';
export const validConstantDoubleSingleString: ConvertToConstantCase<'DoubleWord-String'> =
'DOUBLEWORD_STRING';

// @ts-expect-error - Incorrect case - should be 'SINGLE'
export const invalidConstantSingleString: ConvertToConstantCase<'Single'> = 'single';
export const invalidConstantSingleString: ConvertToConstantCase<'Single'> =
'single';

/**
* ValidateHttpHeaderCase tests
*/
// Valid
export const validHttpHeaderSingleString: ValidateHttpHeaderCase<'Single'> = 'Single';
export const validHttpHeaderSingleSingleString: ValidateHttpHeaderCase<'Double-String'> = 'Double-String';
export const validHttpHeaderDoubleSingleString: ValidateHttpHeaderCase<'DoubleWord-String'> = 'DoubleWord-String';
export const validHttpHeaderUnion1String: ValidateHttpHeaderCase<'First-String' | 'Second-String'> = 'First-String';
export const validHttpHeaderUnion2String: ValidateHttpHeaderCase<'First-String' | 'Second-String'> = 'Second-String';
export const validHttpHeaderSingleString: ValidateHttpHeaderCase<'Single'> =
'Single';
export const validHttpHeaderSingleSingleString: ValidateHttpHeaderCase<'Double-String'> =
'Double-String';
export const validHttpHeaderDoubleSingleString: ValidateHttpHeaderCase<'DoubleWord-String'> =
'DoubleWord-String';
export const validHttpHeaderUnion1String: ValidateHttpHeaderCase<
'First-String' | 'Second-String'
> = 'First-String';
export const validHttpHeaderUnion2String: ValidateHttpHeaderCase<
'First-String' | 'Second-String'
> = 'Second-String';
enum TestCapitalHeaderEnum {
SINGLE = 'Single',
INVALID = 'invalid-string',
DOUBLE_STRING = 'Double-String',
DOUBLEWORD_STRING = 'DoubleWord-String',
}
export const validHttpHeaderSingleEnum: ValidateHttpHeaderCase<TestCapitalHeaderEnum.SINGLE> = 'Single';
export const validHttpHeaderSingleEnum: ValidateHttpHeaderCase<TestCapitalHeaderEnum.SINGLE> =
'Single';
export const validHttpHeaderSingleSingleEnum: ValidateHttpHeaderCase<TestCapitalHeaderEnum.DOUBLE_STRING> =
'Double-String';
export const validHttpHeaderDoubleSingleEnum: ValidateHttpHeaderCase<TestCapitalHeaderEnum.DOUBLEWORD_STRING> =
'DoubleWord-String';

// @ts-expect-error - Incorrect case - 'invalid-string' literal type is not Capital-Case
export const invalidHttpHeaderSingleString: ValidateHttpHeaderCase<'invalid-string'> = 'Invalid';
export const invalidHttpHeaderSingleString: ValidateHttpHeaderCase<'invalid-string'> =
'Invalid';
// @ts-expect-error - Incorrect case - 'invalid-string' union type is not Capital-Case
export const invalidHttpHeaderUnionString: ValidateHttpHeaderCase<'First-String' | 'invalid-string'> = 'invalid-string';
export const invalidHttpHeaderUnionString: ValidateHttpHeaderCase<
'First-String' | 'invalid-string'
> = 'invalid-string';
// @ts-expect-error - Incorrect case - 'invalid-string' enum is not Capital-Case
export const invalidHttpHeaderEnumString: ValidateHttpHeaderCase<TestCapitalHeaderEnum.INVALID> = 'invalid';
export const invalidHttpHeaderEnumString: ValidateHttpHeaderCase<TestCapitalHeaderEnum.INVALID> =
'invalid';

/**
* testHeaderEnumValidity Tests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,22 @@ export type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
/**
* Transform S to CONSTANT_CASE.
*/
export type ConvertToConstantCase<S extends string> = S extends `${infer T}-${infer U}`
? `${Uppercase<T>}_${ConvertToConstantCase<U>}`
: Uppercase<S>;
export type ConvertToConstantCase<S extends string> =
S extends `${infer T}-${infer U}`
? `${Uppercase<T>}_${ConvertToConstantCase<U>}`
: Uppercase<S>;

/**
* Validate that S is in Http-Header-Case, and return S if valid, otherwise never.
*/
export type ValidateHttpHeaderCase<S extends string> = S extends `${infer U}-${infer V}`
? U extends Capitalize<U>
? `${U}-${ValidateHttpHeaderCase<V>}`
: never
: S extends Capitalize<S>
? `${S}` // necessary to cast to string literal type for non-hyphenated enum validation
: never;
export type ValidateHttpHeaderCase<S extends string> =
S extends `${infer U}-${infer V}`
? U extends Capitalize<U>
? `${U}-${ValidateHttpHeaderCase<V>}`
: never
: S extends Capitalize<S>
? `${S}` // necessary to cast to string literal type for non-hyphenated enum validation
: never;

/**
* Helper function to test that Header enum keys and values match correct format.
Expand Down Expand Up @@ -53,11 +55,14 @@ export type ValidateHttpHeaderCase<S extends string> = S extends `${infer U}-${i
export function testHttpHeaderEnumValidity<
TEnum extends IConstants,
TValue extends TEnum[keyof TEnum] & string,
IConstants = Record<ConvertToConstantCase<TValue>, ValidateHttpHeaderCase<TValue>>,
IConstants = Record<
ConvertToConstantCase<TValue>,
ValidateHttpHeaderCase<TValue>
>,
>(
testEnum: TEnum &
Record<
Exclude<keyof TEnum, keyof IConstants>,
['Key must be the CONSTANT_CASED version of the Capital-Cased value']
>
>,
) {}
3 changes: 2 additions & 1 deletion libs/application-generic/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ export * from './dtos';
export * from './encryption/index';
export * from './factories/index';
export * from './health/index';
export * from './http';
export * from './instrumentation/index';
export * from './logging/index';
export * from './modules';
export * from './utils/inject-auth-providers';
export * from './profiling';
export * from './resilience';
export * from './services';
export * from './tracing';
export * from './usecases';
export * from './utils';
export * from './utils/inject-auth-providers';
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import got, {
UploadError,
} from 'got';
import { createHmac } from 'node:crypto';

import {
PostActionEnum,
HttpHeaderKeysEnum,
Expand All @@ -28,7 +27,7 @@ import {
isFrameworkError,
} from '@novu/framework/internal';
import { EnvironmentRepository } from '@novu/dal';
import { HttpRequestHeaderKeysEnum, WorkflowOriginEnum } from '@novu/shared';
import { WorkflowOriginEnum } from '@novu/shared';
import {
ExecuteBridgeRequestCommand,
ExecuteBridgeRequestDto,
Expand All @@ -38,6 +37,7 @@ import {
GetDecryptedSecretKeyCommand,
} from '../get-decrypted-secret-key';
import { BRIDGE_EXECUTION_ERROR } from '../../utils';
import { HttpRequestHeaderKeysEnum } from '../../http';

export const DEFAULT_TIMEOUT = 5_000; // 5 seconds
export const DEFAULT_RETRIES_LIMIT = 3;
Expand Down
5 changes: 0 additions & 5 deletions packages/shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,6 @@
"types": "./dist/esm/utils/index.d.js"
}
},
"dependencies": {
"@nestjs/swagger": "7.4.0",
"class-transformer": "0.5.1",
"class-validator": "0.14.1"
},
"devDependencies": {
"@types/bluebird": "^3.5.24",
"@types/jest": "29.5.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,9 @@
import { IsString, IsOptional, IsEnum } from 'class-validator';
import { JobTitleEnum } from '../../types';

export class UpdateExternalOrganizationDto {
@IsOptional()
@IsEnum(JobTitleEnum)
export type UpdateExternalOrganizationDto = {
jobTitle?: JobTitleEnum;

@IsString()
@IsOptional()
domain?: string;

@IsOptional()
language?: string[];

@IsOptional()
frontendStack?: string[];

@IsOptional()
companySize?: string;
}
};
2 changes: 1 addition & 1 deletion packages/shared/src/dto/workflows/step-data.dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { JSONSchema } from 'json-schema-to-ts';
import type { JSONSchema } from 'json-schema-to-ts';

export type StepDataDto = {
controls: ControlsMetadata;
Expand Down
30 changes: 6 additions & 24 deletions packages/shared/src/dto/workflows/workflow-commons-fields.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { IsArray, IsBoolean, IsDefined, IsOptional, IsString } from 'class-validator';
import { WorkflowResponseDto } from './workflow-response-dto';
import { Slug, StepTypeEnum, WorkflowPreferences } from '../../types';

Expand Down Expand Up @@ -30,34 +29,17 @@ export type WorkflowListResponseDto = Pick<
stepTypeOverviews: StepTypeEnum[];
};

export class StepDto {
@IsString()
@IsDefined()
export type StepDto = {
name: string;

@IsString()
@IsDefined()
type: StepTypeEnum;
}

export class WorkflowCommonsFields {
@IsOptional()
@IsArray()
@IsString({ each: true })
tags?: string[];

@IsOptional()
@IsBoolean()
active?: boolean;
};

@IsString()
@IsDefined()
export type WorkflowCommonsFields = {
tags: string[];
active: boolean;
name: string;

@IsString()
@IsOptional()
description?: string;
}
};

export type PreferencesResponseDto = {
user: WorkflowPreferences | null;
Expand Down
Loading

0 comments on commit c379f2e

Please sign in to comment.