Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 4 additions & 9 deletions docs/packages/node-mongo/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ interface ServiceOptions {
outbox?: boolean,
collectionOptions?: CollectionOptions;
collectionCreateOptions?: CreateCollectionOptions;
privateFields?: string[];
}
```

Expand All @@ -214,6 +215,7 @@ interface ServiceOptions {
|`escapeRegExp`|Escape `$regex` values to prevent special characters from being interpreted as patterns. |`false`|
|`collectionOptions`|MongoDB [CollectionOptions](https://mongodb.github.io/node-mongodb-native/4.10/interfaces/CollectionOptions.html)|`{}`|
|`collectionCreateOptions`|MongoDB [CreateCollectionOptions](https://mongodb.github.io/node-mongodb-native/4.10/interfaces/CreateCollectionOptions.html)|`{}`|
|`privateFields`|Fields that should be omitted from the public response.|`[]`|

### `CreateConfig`
Overrides `ServiceOptions` parameters for create operations.
Expand Down Expand Up @@ -281,19 +283,11 @@ Extending API for a single service.
```typescript
const service = db.createService<User>('users', {
schemaValidator: (obj) => schema.parseAsync(obj),
privateFields: ['passwordHash', 'signupToken', 'resetPasswordToken'],
});

const privateFields = [
'passwordHash',
'signupToken',
'resetPasswordToken',
];

const getPublic = (user: User | null) => _.omit(user, privateFields);

export default Object.assign(service, {
updateLastRequest,
getPublic,
});
```

Expand Down Expand Up @@ -321,6 +315,7 @@ function createService<T extends IDocument>(collectionName: string, options: Ser

const userService = createService<UserType>('users', {
schemaValidator: (obj) => schema.parseAsync(obj),
privateFields: ['passwordHash', 'signupToken', 'resetPasswordToken'],
});

await userService.createOrUpdate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { User } from './user.types';

const service = db.createService<User>(DATABASE_DOCUMENTS.USERS, {
schemaValidator: (obj) => schema.parseAsync(obj),
privateFields: ['passwordHash', 'signupToken', 'resetPasswordToken'],
});

const updateLastRequest = (_id: string) => {
Expand All @@ -21,13 +22,7 @@ const updateLastRequest = (_id: string) => {
);
};

export const privateFields = [
'passwordHash',
'signupToken',
'resetPasswordToken',
];

const getPublic = (user: User | null) => _.omit(user, privateFields);
const getPublic = (user: User | null) => service.getPublic(user);

export default Object.assign(service, {
updateLastRequest,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { User } from 'types';

const service = db.createService<User>(DATABASE_DOCUMENTS.USERS, {
schemaValidator: (obj) => userSchema.parseAsync(obj),
privateFields: ['passwordHash', 'signupToken', 'resetPasswordToken'],
});

const updateLastRequest = (_id: string) =>
Expand All @@ -20,9 +21,7 @@ const updateLastRequest = (_id: string) =>
},
);

const privateFields = ['passwordHash', 'signupToken', 'resetPasswordToken'];

const getPublic = (user: User | null) => _.omit(user, privateFields);
const getPublic = (user: User | null) => service.getPublic(user);

export default Object.assign(service, {
updateLastRequest,
Expand Down
5 changes: 2 additions & 3 deletions packages/node-mongo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1046,11 +1046,10 @@ Extending API for a single service.
```typescript
const service = db.createService<User>("users", {
schemaValidator: (obj) => schema.parseAsync(obj),
privateFields: ['passwordHash', 'signupToken', 'resetPasswordToken'],
});

const privateFields = ["passwordHash", "signupToken", "resetPasswordToken"];

const getPublic = (user: User | null) => _.omit(user, privateFields);
const getPublic = (user: User | null) => service.getPublic(user);

export default Object.assign(service, {
updateLastRequest,
Expand Down
6 changes: 5 additions & 1 deletion packages/node-mongo/src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
} from './types';

import logger from './utils/logger';
import { addUpdatedOnField, generateId } from './utils/helpers';
import { addUpdatedOnField, generateId, omitPrivateFields } from './utils/helpers';
import PopulateUtil from './utils/populate';

import { inMemoryPublisher } from './events/in-memory';
Expand Down Expand Up @@ -977,6 +977,10 @@ class Service<T extends IDocument> {
this.collection = null;
}
};

getPublic = <U extends T = T>(doc: U | null): Partial<U> | null => {
return omitPrivateFields(doc, this.options.privateFields);
};
}

export default Service;
38 changes: 38 additions & 0 deletions packages/node-mongo/src/tests/service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ const companyService = database.createService<CompanyType>('companies', {
schemaValidator: (obj) => companySchema.parseAsync(obj),
});

const usersServicePrivateFields = database.createService<UserType>('usersPrivateFields', {
schemaValidator: (obj) => userSchema.parseAsync(obj),
privateFields: ['fullName', 'age'],
});

describe('service.ts', () => {
before(async () => {
await database.connect();
Expand Down Expand Up @@ -1491,4 +1496,37 @@ describe('service.ts', () => {
updatedUser?.permissions?.[1]?.should.be.undefined;
updatedUser?.permissions?.length?.should.be.equal(0);
});

it('should omit private fields using array configuration', async () => {
const user = await usersServicePrivateFields.insertOne({
fullName: 'John Doe',
age: 30,
role: UserRoles.ADMIN,
});

const publicUser = usersServicePrivateFields.getPublic(user);

(publicUser?.fullName === undefined).should.be.equal(true);
(publicUser?.age === undefined).should.be.equal(true);
publicUser?.role?.should.be.equal(UserRoles.ADMIN);
});

it('should return original document when no privateFields configured', async () => {
const user = await usersService.insertOne({
fullName: 'Test User',
age: 30,
role: UserRoles.ADMIN,
});

const publicUser = usersService.getPublic(user);

publicUser?.fullName?.should.be.equal('Test User');
publicUser?.age?.should.be.equal(30);
publicUser?.role?.should.be.equal(UserRoles.ADMIN);
});

it('should handle null documents in getPublic', async () => {
const publicUser = usersServicePrivateFields.getPublic(null);
(publicUser === null).should.be.equal(true);
});
});
1 change: 1 addition & 0 deletions packages/node-mongo/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ interface ServiceOptions {
collectionOptions?: CollectionOptions;
collectionCreateOptions?: CreateCollectionOptions;
escapeRegExp?: boolean;
privateFields?: string[];
}

export type UpdateFilterFunction<U> = (doc: U) => Partial<U>;
Expand Down
13 changes: 12 additions & 1 deletion packages/node-mongo/src/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,15 @@ const addUpdatedOnField = <T>(update: UpdateFilter<T>): UpdateFilter<T> => {
} as unknown as UpdateFilter<T>;
};

export { deepCompare, generateId, addUpdatedOnField };
const omitPrivateFields = <T extends Record<string, any>>(
doc: T | null | undefined,
privateFields?: string[],
): Partial<T> | null => {
if (!doc) return null;

if (!privateFields) return doc;

return _.omit(doc, privateFields) as Partial<T>;
};

export { deepCompare, generateId, addUpdatedOnField, omitPrivateFields };
5 changes: 2 additions & 3 deletions template/apps/api/src/resources/user/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { User } from 'types';
const service = db.createService<User>(DATABASE_DOCUMENTS.USERS, {
schemaValidator: (obj) => userSchema.parseAsync(obj),
escapeRegExp: true,
privateFields: ['passwordHash'],
});

service.createIndex({ email: 1 }, { unique: true });
Expand All @@ -23,9 +24,7 @@ const updateLastRequest = (_id: string) =>
},
);

const privateFields = ['passwordHash'];

const getPublic = (user: User | null) => _.omit(user, privateFields);
const getPublic = (user: User | null) => service.getPublic(user);

export default Object.assign(service, {
updateLastRequest,
Expand Down
Loading