3.4.1 • Published 5 months ago

@rws-framework/db v3.4.1

Weekly downloads
-
License
ISC
Repository
github
Last release
5 months ago

Models

RWS models are converted to Prisma schemas and are wrapping around generated PrismaClient providing complete typing and better Relation handling + TimeSeries in future versions.

Models index file

import ApiKey from "./ApiKey";
import User from "./User";

export const models = [ User, ApiKey];

Example user model

    import { TrackType, InverseRelation, RWSCollection, RWSModel } from '@rws-framework/db';

    import IUser from './interfaces/IUser';
    import 'reflect-metadata';

    import ApiKey from './ApiKey';
    import IApiKey from './interfaces/IApiKey';

@RWSCollection('users', {
    ignored_keys: ['passwd']
})
class User extends RWSModel<User> implements IUser {
    @TrackType(String)
    username: string;

    @TrackType(String) // Can also handle Object and Number
    passwd: string;

    @TrackType(Boolean)
    active: boolean;

    @TrackType(Date, { required: true })
    created_at: Date;
  
    @TrackType(Date)
    updated_at: Date;

    /**
     * Every relation and inverse relation decorator 
     * uses arrow function model passing
     **/
    @InverseRelation(() => ApiKey, () => User)
    apiKeys: IApiKey[];

    constructor(data?: IUser) {   
        super(data);    

        if(!this.created_at){
            this.created_at = new Date();
        }      
    }
}

//Must export default for automated DI / build work.
export default User;

Relations

Basic many to one relation

import { RWSCollection, RWSModel, TrackType, Relation } from '@rws-framework/db';

import 'reflect-metadata';
import User from './User';
import SomeModel from './SomeModel';
import IApiKey from './interfaces/IApiKey';

@RWSCollection('user_api_keys', {
    relations: {
        dummyIgnoredHydrationRelation: false // ignoring this relation on hydration - will be null
    }
})
class ApiKey extends RWSModel<ApiKey> implements IApiKey {

    @Relation(() => User, { requried: false }) // second attribute is required = false
    user: User;

    @Relation(() => SomeModel) // relation to be ignored by 
    dummyIgnoredHydrationRelation: SomeModel;

    @TrackType(Object)
    keyval: string;

    @TrackType(Date, { required: true })
    created_at: Date;
  
    @TrackType(Date)
    updated_at: Date;

    static _collection = 'api_keys';

    constructor(data?: IApiKey) {   
        super(data);    

        if(!this.created_at){
            this.created_at = new Date();
        }    

        this.updated_at = new Date();
    }    
}

export default ApiKey;

Relation decorator (many-to-one)

import 'reflect-metadata';
import { RWSModel, OpModelType } from '../models/_model';

export type CascadingSetup = 'Cascade' | 'Restrict' | 'NoAction' | 'SetNull';

export interface IRelationOpts {
    required?: boolean
    key: string
    relationField: string //name of field that will hold the relation key value
    relatedToField?: string //name of related field (id by default)
    mappingName?: string
    relatedTo: OpModelType<RWSModel<any>>
    many?: boolean // is it one-to-many or many-to-one
    embed?: boolean // @deprecated for mongo - new decorator for embeds incoming
    useUuid?: boolean //for sql dbs - if you're using some text based id
    relationName?: string
    cascade?: {
        onDelete?: CascadingSetup,
        onUpdate?: CascadingSetup
    }
}

const _DEFAULTS: Partial<IRelationOpts> = { required: false, many: false, embed: false, cascade: { onDelete: 'SetNull', onUpdate: 'Cascade' }};
  
function Relation(theModel: () => OpModelType<RWSModel<any>>, relationOptions: Partial<IRelationOpts> = _DEFAULTS) {
    return function(target: any, key: string) {     
        // Store the promise in metadata immediately
        
        const metadataPromise = Promise.resolve().then(() => {            
            const relatedTo = theModel();

            const metaOpts: IRelationOpts = {
                ...relationOptions, 
                cascade: relationOptions.cascade || _DEFAULTS.cascade,
                relatedTo,
                relationField: relationOptions.relationField ? relationOptions.relationField : relatedTo._collection + '_id',
                key,
                // Generate a unique relation name if one is not provided
                relationName: relationOptions.relationName ? 
                  relationOptions.relationName.toLowerCase() : 
                  `${target.constructor.name.toLowerCase()}_${key}_${relatedTo._collection.toLowerCase()}`
            };  
            
            if(relationOptions.required){
                metaOpts.cascade.onDelete = 'Restrict';
            }

            return metaOpts;
        });

        // Store both the promise and the key information
        Reflect.defineMetadata(`Relation:${key}`, {
            promise: metadataPromise,
            key
        }, target);
    };
}


export default Relation;

Inverse relation decorator (one-to-many)

import 'reflect-metadata';
import { RWSModel, OpModelType } from '../models/_model';

export interface InverseRelationOpts {
    key: string,
    inversionModel: OpModelType<RWSModel<any>>
    foreignKey: string
    singular?: boolean
    relationName?: string
    mappingName?: string
}

function InverseRelation(inversionModel: () => OpModelType<RWSModel<any>>, sourceModel: () => OpModelType<RWSModel<any>>, relationOptions: Partial<InverseRelationOpts> = null) {
    return function (target: any, key: string) {
        const metadataPromise = Promise.resolve().then(() => {
            const model = inversionModel();
            const source = sourceModel();

            const metaOpts: InverseRelationOpts = {
                ...relationOptions,
                key,
                inversionModel: model,
                foreignKey: relationOptions && relationOptions.foreignKey ? relationOptions.foreignKey : `${source._collection}_id`,
                // Generate a unique relation name if one is not provided
                relationName: relationOptions && relationOptions.relationName ?
                    relationOptions.relationName.toLowerCase() :
                    `${model._collection}_${key}_${source._collection}`.toLowerCase()
            };

            return metaOpts;
        });

        // Store both the promise and the key information
        Reflect.defineMetadata(`InverseRelation:${key}`, {
            promise: metadataPromise,
            key
        }, target);
    };
}

export default InverseRelation;

RWS Model to prisma conversion

Init helper class

This needs to be run either from the package or CLI - it changes models to prisma schema and registers it.

IDbConfigHandler - An interface for config bag for DBService

    import { OpModelType } from "../models/_model";

    export interface IDbConfigParams {
        mongo_url?: string;
        mongo_db?: string;
        db_models?: OpModelType<any>[]
    }

    export interface IDbConfigHandler {
        get<K extends keyof IDbConfigParams>(key: K): IDbConfigParams[K];
    }

Helper prisma install example

class Config implements IDbConfigHandler {
    private data: IDbConfigParams = {
        db_models: [],
        db_name: null,
        db_url: null,
        db_type: null
    };

    private modelsDir: string;
    private cliExecRoot: string;
    private static _instance: Config = null;

    private constructor(){}

    static async getInstance(): Promise<Config>
    {
        if(!this._instance){
        this._instance = new Config();
        }    

        await this._instance.fill();

        return this._instance;
    }

    
    async fill(): Promise<void>
    {
        this.data.db_url = args[0];
        this.data.db_name = args[1];    
        this.data.db_type = args[2];
        

        this.modelsDir = args[3];    
        this.cliExecRoot = args[4]; 
    }

    getModelsDir(): string
    {
        return this.modelsDir
    }

    getCliExecRoot(): string
    {
        return this.cliExecRoot
    }

    get<K extends keyof IDbConfigParams>(key: K): IDbConfigParams[K] {
        return this.data[key];  
    }
    }

    async function main(): Promise<void>
    {
        console.log('INSTALL PRISMA');
        const cfg = await Config.getInstance();
        DbHelper.installPrisma(cfg, new DBService(cfg), false);
    }

Init CLI

Basic CLI command that executes generateModelSections() from conversion script is:

rws-db DB_URL MODELS_DIRECTORY

The exec file.

/exec/src/console.js

dbType can be any prisma db driver - mongodb by default

#npm

npx rws-db "mongodb://user:pass@localhost:27017/databaseName?authSource=admin&replicaSet=rs0" databaseName dbType src/models
#yarn

yarn rws-db "mongodb://user:pass@localhost:27017/databaseName?authSource=admin&replicaSet=rs0" databaseName dbType src/models
#bun

bunx rws-db "mongodb://user:pass@localhost:27017/databaseName?authSource=admin&replicaSet=rs0" databaseName dbType src/models

Code for RWS to prisma conversion from "@rws-framework/server" package:

The repo file

3.4.1

5 months ago

3.3.8

6 months ago

3.3.7

6 months ago

3.3.6

6 months ago

3.3.5

6 months ago

3.3.4

6 months ago

3.3.3

6 months ago

3.3.2

6 months ago

3.3.1

6 months ago

3.3.0

6 months ago

3.2.0

6 months ago

3.1.0

6 months ago

3.0.1

6 months ago

3.0.0

6 months ago

2.3.8

7 months ago

2.3.7

7 months ago

2.3.6

7 months ago

2.3.5

7 months ago

2.3.4

7 months ago

2.4.6

7 months ago

2.4.5

7 months ago

2.4.4

7 months ago

2.4.3

7 months ago

2.4.2

7 months ago

2.4.1

7 months ago

2.4.0

7 months ago

2.3.2

7 months ago

2.3.1

7 months ago

2.3.0

7 months ago

2.2.10

7 months ago

2.2.8

7 months ago

2.2.7

7 months ago

2.2.6

7 months ago

2.2.5

7 months ago

2.2.4

7 months ago

2.2.3

7 months ago

2.2.1

7 months ago

2.2.0

7 months ago

2.1.11

8 months ago

2.1.10

8 months ago

2.1.9

8 months ago

2.1.8

8 months ago

2.1.7

8 months ago

2.1.6

8 months ago

2.1.5

8 months ago

2.1.4

8 months ago

2.1.2

8 months ago

2.1.1

8 months ago

2.1.0

8 months ago

2.0.5

9 months ago

2.0.4

9 months ago

2.0.3

9 months ago

2.0.2

9 months ago

2.0.1

9 months ago

2.0.0

9 months ago

1.0.1

9 months ago