1.0.0 • Published 1 year ago

@v-sandbox/shared-graphql-constants v1.0.0

Weekly downloads
-
License
UNLICENSED
Repository
github
Last release
1 year ago

@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();
    }
}