Skip to content

Commit

Permalink
feat(database)!: crud granularity (#180)
Browse files Browse the repository at this point in the history
  • Loading branch information
sdimitris authored May 31, 2022
1 parent 8ac2d96 commit 86cfa37
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 35 deletions.
2 changes: 1 addition & 1 deletion libraries/grpc-sdk/src/classes/Routing/RouteBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class RouteBuilder {
if (!Array.isArray(middleware)) {
middleware = [middleware];
}
if (this._options.middlewares?.length !== 0) {
if (this._options.middlewares !== undefined && this._options.middlewares?.length !== 0) {
if (allowDuplicates) {
this._options.middlewares?.concat(middleware);
} else {
Expand Down
40 changes: 36 additions & 4 deletions modules/database/src/admin/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,24 @@ export class AdminHandlers {
fields: ConduitJson.Required,
modelOptions: ConduitJson.Optional,
enabled: ConduitBoolean.Optional, // move inside modelOptions (frontend-compat)
authentication: ConduitBoolean.Optional, // move inside modelOptions (frontend-compat)
crudOperations: ConduitBoolean.Optional, // move inside modelOptions (frontend-compat)
crudOperations: {
create: {
enabled: ConduitBoolean.Optional,
authenticated: ConduitBoolean.Required,
},
read: {
enabled: ConduitBoolean.Optional,
authenticated: ConduitBoolean.Required,
},
update: {
enabled: ConduitBoolean.Optional,
authenticated: ConduitBoolean.Required,
},
delete: {
enabled: ConduitBoolean.Optional,
authenticated: ConduitBoolean.Required,
},
},
permissions: {
extendable: ConduitBoolean.Optional,
canCreate: ConduitBoolean.Optional,
Expand All @@ -180,8 +196,24 @@ export class AdminHandlers {
fields: ConduitJson.Optional,
modelOptions: ConduitJson.Optional,
enabled: ConduitBoolean.Optional, // move inside modelOptions (frontend-compat)
authentication: ConduitBoolean.Optional, // move inside modelOptions (frontend-compat)
crudOperations: ConduitBoolean.Optional, // move inside modelOptions (frontend-compat)
crudOperations: {
create: {
enabled: ConduitBoolean.Optional,
authenticated: ConduitBoolean.Optional,
},
read: {
enabled: ConduitBoolean.Optional,
authenticated: ConduitBoolean.Optional,
},
update: {
enabled: ConduitBoolean.Optional,
authenticated: ConduitBoolean.Optional,
},
delete: {
enabled: ConduitBoolean.Optional,
authenticated: ConduitBoolean.Optional,
},
},
permissions: {
extendable: ConduitBoolean.Optional,
canCreate: ConduitBoolean.Optional,
Expand Down
1 change: 0 additions & 1 deletion modules/database/src/admin/documents.admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ export class DocumentsAdmin {
if (isNil(schema)) {
throw new GrpcError(status.NOT_FOUND, 'Schema does not exist');
}

if (!query || query.length === '') {
query = {};
}
Expand Down
15 changes: 7 additions & 8 deletions modules/database/src/admin/schema.admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,7 @@ export class SchemaAdmin {
permissions,
} = call.request.params;
const enabled = call.request.params.enabled ?? true;
const authentication = call.request.params.authentication ?? false;
const crudOperations = call.request.params.crudOperations ?? true;
const crudOperations = call.request.params.crudOperations;

if (name.indexOf('-') >= 0 || name.indexOf(' ') >= 0) {
throw new GrpcError(
Expand Down Expand Up @@ -153,8 +152,8 @@ export class SchemaAdmin {
});

const schemaOptions = isNil(modelOptions)
? { conduit: { cms: { enabled, authentication, crudOperations } } }
: { ...modelOptions, conduit: { cms: { enabled, authentication, crudOperations } } };
? { conduit: { cms: { enabled, crudOperations } } }
: { ...modelOptions, conduit: { cms: { enabled, crudOperations } } };
schemaOptions.conduit.permissions = permissions; // database sets missing perms to defaults

return this.schemaController
Expand All @@ -168,12 +167,13 @@ export class SchemaAdmin {
}

async patchSchema(call: ParsedRouterRequest): Promise<UnparsedRouterResponse> {
const {
let {
id,
name,
fields,
modelOptions,
permissions,
crudOperations
} = call.request.params;

if (!isNil(name) && name !== '') {
Expand Down Expand Up @@ -201,12 +201,11 @@ export class SchemaAdmin {
requestedSchema.fields = fields ? fields : requestedSchema.fields;
const enabled = call.request.params.enabled ?? requestedSchema.modelOptions.conduit.cms.enabled;

const authentication = call.request.params.authentication ?? requestedSchema.modelOptions.conduit.cms.authentication;
const crudOperations = call.request.params.crudOperations ?? requestedSchema.modelOptions.conduit.cms.crudOperations;
crudOperations = call.request.params.crudOperations ?? requestedSchema.modelOptions.conduit.cms.crudOperations;
requestedSchema.modelOptions = merge(
requestedSchema.modelOptions,
modelOptions,
{ conduit: { cms: { enabled, authentication, crudOperations } } },
{ conduit: { cms: { enabled, crudOperations } } },
);


Expand Down
55 changes: 35 additions & 20 deletions modules/database/src/controllers/cms/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,20 +64,22 @@ function removeRequiredFields(fields: any) {

export function getOps(schemaName: string, actualSchema: any, handlers: CmsHandlers) {
let routesArray: any = [];
const authenticatedRead = actualSchema.modelOptions.conduit.cms.crudOperations.read.authenticated;
const readIsEnabled = actualSchema.modelOptions.conduit.cms.crudOperations.read.enabled;
let route = new RouteBuilder()
.path(`/${schemaName}/:id`)
.method(ConduitRouteActions.GET)
.urlParams({
id: { type: TYPE.String, required: true },
}).cacheControl(actualSchema.authentication
}).cacheControl(authenticatedRead
? 'private, max-age=10'
: 'public, max-age=10')
.return(`${schemaName}`, actualSchema.fields)
.handler(handlers.getDocumentById.bind(handlers));
if (actualSchema.authentication) {
if (authenticatedRead)
route.middleware('authMiddleware');
}
routesArray.push(route.build());
if (readIsEnabled)
routesArray.push(route.build());

route = new RouteBuilder()
.path(`/${schemaName}`)
Expand All @@ -87,18 +89,19 @@ export function getOps(schemaName: string, actualSchema: any, handlers: CmsHandl
limit: TYPE.Number,
sort: [TYPE.String],
})
.cacheControl(actualSchema.authentication
.cacheControl(authenticatedRead
? 'private, max-age=10'
: 'public, max-age=10')
.return(`get${schemaName}`, {
documents: [actualSchema.fields],
count: TYPE.Number,
})
.handler(handlers.getDocuments.bind(handlers));
if (actualSchema.authentication) {
if (authenticatedRead) {
route.middleware('authMiddleware');
}
routesArray.push(route.build());
if (readIsEnabled)
routesArray.push(route.build());

let assignableFields = Object.assign({}, actualSchema.fields);
delete assignableFields._id;
Expand All @@ -110,10 +113,13 @@ export function getOps(schemaName: string, actualSchema: any, handlers: CmsHandl
.bodyParams(assignableFields)
.return(`create${schemaName}`, actualSchema.fields)
.handler(handlers.createDocument.bind(handlers));
if (actualSchema.authentication) {
const authenticatedCreate = actualSchema.modelOptions.conduit.cms.crudOperations.create.authenticated;
const createIsEnabled = actualSchema.modelOptions.conduit.cms.crudOperations.create.enabled;
if (authenticatedCreate) {
route.middleware('authMiddleware');
}
routesArray.push(route.build());
if (createIsEnabled)
routesArray.push(route.build());

route = new RouteBuilder()
.path(`/${schemaName}/many`)
Expand All @@ -123,10 +129,11 @@ export function getOps(schemaName: string, actualSchema: any, handlers: CmsHandl
docs: [actualSchema.fields],
})
.handler(handlers.createManyDocuments.bind(handlers));
if (actualSchema.authentication) {
if (authenticatedCreate) {
route.middleware('authMiddleware');
}
routesArray.push(route.build());
if (createIsEnabled)
routesArray.push(route.build());

route = new RouteBuilder()
.path(`/${schemaName}/many`)
Expand All @@ -141,10 +148,13 @@ export function getOps(schemaName: string, actualSchema: any, handlers: CmsHandl
docs: [actualSchema.fields],
})
.handler(handlers.updateManyDocuments.bind(handlers));
if (actualSchema.authentication) {
const authenticatedUpdate = actualSchema.modelOptions.conduit.cms.crudOperations.update.authenticated;
const updateIsEnabled = actualSchema.modelOptions.conduit.cms.crudOperations.update.enabled;
if (authenticatedUpdate) {
route.middleware('authMiddleware');
}
routesArray.push(route.build());
if (updateIsEnabled)
routesArray.push(route.build());

route = new RouteBuilder()
.path(`/${schemaName}/many`)
Expand All @@ -164,10 +174,11 @@ export function getOps(schemaName: string, actualSchema: any, handlers: CmsHandl
docs: [actualSchema.fields],
})
.handler(handlers.patchManyDocuments.bind(handlers));
if (actualSchema.authentication) {
if (authenticatedUpdate) {
route.middleware('authMiddleware');
}
routesArray.push(route.build());
if (updateIsEnabled)
routesArray.push(route.build());

route = new RouteBuilder()
.path(`/${schemaName}/:id`)
Expand All @@ -178,7 +189,7 @@ export function getOps(schemaName: string, actualSchema: any, handlers: CmsHandl
.bodyParams(assignableFields)
.return(`update${schemaName}`, actualSchema.fields)
.handler(handlers.updateDocument.bind(handlers));
if (actualSchema.authentication) {
if (authenticatedUpdate) {
route.middleware('authMiddleware');
}
routesArray.push(route.build());
Expand All @@ -192,10 +203,11 @@ export function getOps(schemaName: string, actualSchema: any, handlers: CmsHandl
.bodyParams(removeRequiredFields(Object.assign({}, assignableFields)))
.return(`patch${schemaName}`, actualSchema.fields)
.handler(handlers.patchDocument.bind(handlers));
if (actualSchema.authentication) {
if (authenticatedUpdate) {
route.middleware('authMiddleware');
}
routesArray.push(route.build());
if (updateIsEnabled)
routesArray.push(route.build());
route = new RouteBuilder()
.path(`/${schemaName}/:id`)
.method(ConduitRouteActions.DELETE)
Expand All @@ -204,10 +216,13 @@ export function getOps(schemaName: string, actualSchema: any, handlers: CmsHandl
})
.return(`delete${schemaName}`, TYPE.String)
.handler(handlers.deleteDocument.bind(handlers));
if (actualSchema.authentication) {
const authenticatedDelete = actualSchema.modelOptions.conduit.cms.crudOperations.delete.authenticated;
const deleteIsEnabled = actualSchema.modelOptions.conduit.cms.crudOperations.delete.enabled;
if (authenticatedDelete) {
route.middleware('authMiddleware');
}
routesArray.push(route.build());
if (deleteIsEnabled)
routesArray.push(route.build());

return routesArray;
}
Expand Down
40 changes: 40 additions & 0 deletions modules/database/src/migrations/crudOperations.migration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { DatabaseAdapter } from '../adapters/DatabaseAdapter';
import { MongooseSchema } from '../adapters/mongoose-adapter/MongooseSchema';
import { SequelizeSchema } from '../adapters/sequelize-adapter/SequelizeSchema';

export async function migrateCrudOperations(adapter: DatabaseAdapter<MongooseSchema | SequelizeSchema>) {
const model = adapter.getSchemaModel('_DeclaredSchema').model;
const cmsSchemas = await model
.findMany({ 'modelOptions.conduit.cms.enabled': { $exists: true } });

for (const schema of cmsSchemas) {
const { crudOperations, authentication, enabled } = schema.modelOptions.conduit.cms;
const cms = {
enabled: enabled,
crudOperations: {
create: {
enabled: crudOperations,
authenticated: authentication,
},
read: {
enabled: crudOperations,
authenticated: authentication,
},
update: {
enabled: crudOperations,
authenticated: authentication,
},
delete: {
enabled: crudOperations,
authenticated: authentication,
},
},
};
const id = (schema._id).toString()
await model.findByIdAndUpdate(id,{
modelOptions: {
conduit: { cms }
},
});
}
}
3 changes: 2 additions & 1 deletion modules/database/src/migrations/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { DatabaseAdapter } from '../adapters/DatabaseAdapter';
import { MongooseSchema } from '../adapters/mongoose-adapter/MongooseSchema';
import { SequelizeSchema } from '../adapters/sequelize-adapter/SequelizeSchema';
import { migrateCrudOperations } from './crudOperations.migration';

export async function runMigrations(adapter: DatabaseAdapter<MongooseSchema | SequelizeSchema>){
// ...
await migrateCrudOperations(adapter);
}

0 comments on commit 86cfa37

Please sign in to comment.