1.0.0 • Published 2 years ago
@v-sandbox/shared-graphql-constants v1.0.0
@v-sandbox/shared-graphql-constants
1. The shared Timestamp Model:
- TimestampModel
- ITimestampModel
2. The shared User Relation Model:
- UserRelationModel
- IUserRelationModel
3 The shared Content Base Model:
- ContentBaseModel
- IContentBaseModel
import { Schema } from 'mongoose';
export declare abstract class ITimestampModel {
_id?: string;
created_at?: Date;
last_updated_at?: Date;
deleted?: boolean;
static get schema(): Schema;
static softDelete: (_id: string, user_id?: string) => Promise<boolean>;
}
export declare abstract class TimestampModel extends ITimestampModel {
}
import { ITimestampModel } from "@blipiqlabs/edu-shared-graphql-constants";
export declare abstract class IUserRelationModel extends ITimestampModel {
created_by?: string;
last_updated_by?: string;
org_id?: string;
}
export declare abstract class UserRelationModel extends IUserRelationModel {
}
import { IUserRelationModel } from "@blipiqlabs/edu-shared-graphql-constants";
import { ContentBaseModelStatus } from "@blipiqlabs/edu-shared-graphql-constants";
export declare abstract class IContentBaseModel extends IUserRelationModel {
status: ContentBaseModelStatus;
status_timestamp: Date;
}
export declare abstract class ContentBaseModel extends IContentBaseModel {
}
4. The shared Base Service:
- BaseService
import { MongoError } from 'mongodb';
import { CreateQuery, Document, FilterQuery, Model, Types, UpdateQuery } from 'mongoose';
import { UserRelationModel, TimestampModel } from "@blipiqlabs/edu-shared-graphql-constants";
import { TakePaginationInput } from "../common/take-pagination.input";
export declare abstract class BaseService<T extends TimestampModel | UserRelationModel | ContentBaseModel> {
model: Model<T>;
protected constructor(model: Model<T>);
protected static throwMongoError(err: MongoError): void;
delete(_id: string, user_id: string): Promise<boolean>;
protected static toObjectId(id: string): Types.ObjectId;
_findOne(filter?: {}): Promise<T>;
findOne(filter?: {}): Promise<T>;
_findById(id: string): Promise<T>;
findById(id: string): Promise<T>;
_findOneAndUpdate(query: FilterQuery<T & Document>, item: UpdateQuery<T & Document>, options?: QueryFindOneAndUpdateOptions): Promise<T>;
findOneAndUpdate(query: FilterQuery<T & Document>, item: UpdateQuery<T & Document>, options?: QueryFindOneAndUpdateOptions): Promise<T>;
_create(item: CreateQuery<T>): Promise<T>;
create(item: CreateQuery<T>): Promise<T>;
_deleteById(id: string): Promise<T>;
deleteById(id: string): Promise<T>;
_update(item: T, options?: QueryFindOneAndUpdateOptions): Promise<T>;
update(item: Partial<T>, options?: QueryFindOneAndUpdateOptions): Promise<T>;
_updateMany(filter: FilterQuery<T>, item: Partial<T>, options?: QueryFindOneAndUpdateOptions): Promise<Promise<T[]> | Query<T[]>>;
private _count;
count(filter?: {}): Promise<number>;
_find(item?: {}): Promise<Array<T>>;
find(item?: FilterQuery<T>): Promise<Array<T>>;
private filterQuery;
getDocuments(filter?: FilterQuery<T>, pagination?: PaginationInput, populate?: QueryPopulateOptions | QueryPopulateOptions[]): Promise<PaginatedModel<T>>;
getDocumentsByAtlasSearch(queryString?: string, filter?: FilterQuery<T>, pagination?: PaginationInput): Promise<PaginatedModel<T>>;
private getMatchStage;
private static transformQueryStringToArray;
private getTotalDocumentsByAtlasSearch;
private getAllDocumentsByAtlasSearch;
private getLimitedDocumentsByAtlasSearch;
private getPageInfo;
}
5. The shared functionality:
- Paginated
import { Field, ObjectType, Int } from '@nestjs/graphql';
import { Type } from '@nestjs/common';
@ObjectType()
export class PageInfo {
@Field(() => Boolean)
hasNextPage: boolean;
@Field(() => String, { nullable: true })
after?: string;
}
export function Paginated<T>(classRef: Type<T>): any {
@ObjectType({ isAbstract: true })
abstract class PaginatedType {
@Field(() => [classRef], { nullable: true })
docs: T[];
@Field(() => Int)
total: number;
@Field(() => PageInfo)
pageInfo: PageInfo;
}
return PaginatedType;
}
6. The shared helper methods:
-escapeRegExp
7. The shared types:
- DifficultyLevel
- AgeGroup
@InputType('DifficultyLevelInput')
@ObjectType('DifficultyLevelType')
@modelOptions({ options: { allowMixed: Severity.ALLOW }, schemaOptions: { _id: false } })
export class DifficultyLevel {
@Field(() => Int)
@prop()
public level: number;
@Field(() => String)
@prop()
public text: string;
}
@InputType('AgeGroupInput')
@ObjectType('AgeGroupType')
@modelOptions({ options: { allowMixed: Severity.ALLOW }, schemaOptions: { _id: false } })
export class AgeGroup {
@Field(() => Int)
@prop()
public id: number;
@Field(() => String)
@prop()
public name: string;
}
8. The shared decorators:
- OrgId
- Authorization
- User
- ActionContext
export const OrgId = (options?: Omit<ArgsOptions, 'type'>) => {
const {nullable = true, ...restOptions} = options ?? {};
return Args('org_id', {
...restOptions,
nullable,
type: () => String
});
};
export const Authorization = createParamDecorator(
(data: unknown, context: ExecutionContext) => {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req.headers.authorization;
}
);
export const User = createParamDecorator(
(data: unknown, context: ExecutionContext) => {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req.body.user;
}
);
export const ActionContext = createParamDecorator(
(data: unknown, context: ExecutionContext) => {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req.actionContext;
}
);
9. The shared interceptors:
- OrgIdInterceptor
@Injectable()
export class OrgIdInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgByIndex(1);
const req = gqlContext.getArgByIndex(2).req;
const { user } = req.body;
const isUserSuperAdmin = !user?.org_id;
if (isUserSuperAdmin) {
/* The super-admin does not belong to any organization, so they must specify org_id in the arguments. */
if (!args?.org_id)
throw new BadRequestException(
AppError.FIELD_SHOULD_BE_PROVIDED('org_id')
);
} else {
/* For other users, org_id is taken from their tokens. */
args.org_id = user.org_id;
}
return next.handle();
}
}
1.0.0
2 years ago