Skip to content

Commit

Permalink
feat(core): add UNIQUE option to model annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
thelinuxlich committed Jan 15, 2022
1 parent b4ee4db commit ece0cc2
Show file tree
Hide file tree
Showing 22 changed files with 341 additions and 136 deletions.
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@
"scripts": {
"preinstall": "npx only-allow pnpm",
"build": "pnpm run -r build",
"bootstrap:redis": "docker run -d -p 6379:6379 -it --rm redislabs/redisgraph",
"ian:error": "pnpm build && "
"bootstrap:redis": "docker run -d -p 6379:6379 -it --rm redislabs/redisgraph"
},
"husky": {
"hooks": {
Expand Down
10 changes: 5 additions & 5 deletions packages/graphback-codegen-schema/dist/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1138,7 +1138,7 @@ var SchemaCRUDPlugin = class extends GraphbackPlugin {
if (!context.graphback || !context.graphback[modelName]) {
throw new Error(`Missing service for ${modelName}`);
}
return context.graphback[modelName].create(args.input, context, info);
return context.graphback[modelName].create(args.input, context, info, model.uniqueFields);
};
}
addUpdateMutationResolver(model, mutationObj) {
Expand All @@ -1148,7 +1148,7 @@ var SchemaCRUDPlugin = class extends GraphbackPlugin {
if (!context.graphback || !context.graphback[modelName]) {
throw new Error(`Missing service for ${modelName}`);
}
return context.graphback[modelName].update(args.input, context, info);
return context.graphback[modelName].update(args.input, context, info, model.uniqueFields);
};
}
addUpdateByMutationResolver(model, mutationObj) {
Expand All @@ -1158,7 +1158,7 @@ var SchemaCRUDPlugin = class extends GraphbackPlugin {
if (!context.graphback || !context.graphback[modelName]) {
throw new Error(`Missing service for ${modelName}`);
}
return context.graphback[modelName].updateBy(args, context, info);
return context.graphback[modelName].updateBy(args, context, info, model.uniqueFields);
};
}
addDeleteMutationResolver(model, mutationObj) {
Expand All @@ -1168,7 +1168,7 @@ var SchemaCRUDPlugin = class extends GraphbackPlugin {
if (!context.graphback || !context.graphback[modelName]) {
throw new Error(`Missing service for ${modelName}`);
}
return context.graphback[modelName].delete(args, context, info);
return context.graphback[modelName].delete(args, context, info, model.uniqueFields);
};
}
addDeleteByMutationResolver(model, mutationObj) {
Expand All @@ -1178,7 +1178,7 @@ var SchemaCRUDPlugin = class extends GraphbackPlugin {
if (!context.graphback || !context.graphback[modelName]) {
throw new Error(`Missing service for ${modelName}`);
}
return context.graphback[modelName].deleteBy(args, context, info);
return context.graphback[modelName].deleteBy(args, context, info, model.uniqueFields);
};
}
addFindQueryResolver(model, queryObj) {
Expand Down
2 changes: 1 addition & 1 deletion packages/graphback-codegen-schema/dist/index.mjs.map

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions packages/graphback-codegen-schema/src/SchemaCRUDPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export class SchemaCRUDPlugin extends GraphbackPlugin {
this.buildSchemaModelRelationships(schemaComposer, models)
this.buildSchemaForModels(schemaComposer, models)
this.addVersionedMetadataFields(schemaComposer, models)

return schemaComposer.buildSchema()
}

Expand Down Expand Up @@ -607,7 +607,7 @@ export class SchemaCRUDPlugin extends GraphbackPlugin {
throw new Error(`Missing service for ${modelName}`)
}

return context.graphback[modelName].create(args.input, context, info)
return context.graphback[modelName].create(args.input, context, info, model.uniqueFields)
}
}

Expand All @@ -626,7 +626,7 @@ export class SchemaCRUDPlugin extends GraphbackPlugin {
throw new Error(`Missing service for ${modelName}`)
}

return context.graphback[modelName].update(args.input, context, info)
return context.graphback[modelName].update(args.input, context, info, model.uniqueFields)
}
}

Expand All @@ -645,7 +645,7 @@ export class SchemaCRUDPlugin extends GraphbackPlugin {
throw new Error(`Missing service for ${modelName}`)
}

return context.graphback[modelName].updateBy(args, context, info)
return context.graphback[modelName].updateBy(args, context, info, model.uniqueFields)
}
}

Expand All @@ -664,7 +664,7 @@ export class SchemaCRUDPlugin extends GraphbackPlugin {
throw new Error(`Missing service for ${modelName}`)
}

return context.graphback[modelName].delete(args, context, info)
return context.graphback[modelName].delete(args, context, info, model.uniqueFields)
}
}

Expand All @@ -683,7 +683,7 @@ export class SchemaCRUDPlugin extends GraphbackPlugin {
throw new Error(`Missing service for ${modelName}`)
}

return context.graphback[modelName].deleteBy(args, context, info)
return context.graphback[modelName].deleteBy(args, context, info, model.uniqueFields)
}
}

Expand Down
31 changes: 19 additions & 12 deletions packages/graphback-core/dist/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,7 @@ var GraphbackCoreMetadata = class {
buildModel(modelType, relationships) {
let crudOptions = parseMetadata7("model", modelType);
crudOptions = Object.assign({}, this.supportedCrudMethods, crudOptions);
const uniqueFields = Array.isArray(crudOptions.unique) ? crudOptions.unique : [];
const { type: primaryKeyType, name } = getPrimaryKey(modelType);
const primaryKey = {
name,
Expand Down Expand Up @@ -700,6 +701,7 @@ var GraphbackCoreMetadata = class {
primaryKey,
crudOptions,
relationships,
uniqueFields,
graphqlType: modelType
};
}
Expand Down Expand Up @@ -1004,9 +1006,12 @@ var CRUDService = class {
this.db = db;
this.pubSub = config.pubSub;
}
async create(data2, context, info) {
async initializeUniqueIndex() {
return await this.db.initializeUniqueIndex(this.model.uniqueFields);
}
async create(data2, context, info, uniqueFields) {
const [selectedFields, _] = getSelectedFieldsFromResolverInfo(info, this.model, true);
const result = await this.db.create(data2, selectedFields);
const result = await this.db.create(data2, selectedFields, uniqueFields);
if (this.pubSub && this.crudOptions.subCreate) {
const topic = this.subscriptionTopicMapping("create" /* CREATE */, this.model.graphqlType.name);
const payload = this.buildEventPayload("new", result);
Expand All @@ -1016,9 +1021,9 @@ var CRUDService = class {
}
return result;
}
async update(data2, context, info) {
async update(data2, context, info, uniqueFields) {
const [selectedFields, _] = getSelectedFieldsFromResolverInfo(info, this.model, true);
const result = await this.db.update(data2, selectedFields);
const result = await this.db.update(data2, selectedFields, uniqueFields);
if (this.pubSub && this.crudOptions.subUpdate) {
const topic = this.subscriptionTopicMapping("update" /* UPDATE */, this.model.graphqlType.name);
const payload = this.buildEventPayload("updated", result);
Expand All @@ -1028,16 +1033,16 @@ var CRUDService = class {
}
return result;
}
async updateBy(args, context, info) {
async updateBy(args, context, info, uniqueFields) {
const [selectedFields, _] = getSelectedFieldsFromResolverInfo(info, this.mode, true);
const result = await this.db.updateBy(args, selectedFields);
const result = await this.db.updateBy(args, selectedFields, uniqueFields);
return {
items: result
};
}
async delete(args, context, info) {
async delete(args, context, info, uniqueFields) {
const [selectedFields, _] = getSelectedFieldsFromResolverInfo(info, this.model, true);
const result = await this.db.delete(data, selectedFields);
const result = await this.db.delete(data, selectedFields, uniqueFields);
if (this.pubSub && this.crudOptions.subDelete) {
const topic = this.subscriptionTopicMapping("delete" /* DELETE */, this.model.graphqlType.name);
const payload = this.buildEventPayload("deleted", result);
Expand All @@ -1047,9 +1052,9 @@ var CRUDService = class {
}
return result;
}
async deleteBy(args, context, info) {
async deleteBy(args, context, info, uniqueFields) {
const [selectedFields, _] = getSelectedFieldsFromResolverInfo(info, this.model, true);
const result = await this.db.deleteBy(args, selectedFields);
const result = await this.db.deleteBy(args, selectedFields, uniqueFields);
return {
items: result
};
Expand Down Expand Up @@ -1141,13 +1146,15 @@ var CRUDService = class {
// src/runtime/createCRUDService.ts
import { PubSub } from "graphql-subscriptions";
function createCRUDService(config) {
return (model, dataProvider) => {
return async (model, dataProvider) => {
const serviceConfig = __spreadProps(__spreadValues({
pubSub: new PubSub()
}, config), {
crudOptions: model.crudOptions
});
return new CRUDService(model, dataProvider, serviceConfig);
const crudService = new CRUDService(model, dataProvider, serviceConfig);
await crudService.initializeUniqueIndex();
return crudService;
};
}

Expand Down
2 changes: 1 addition & 1 deletion packages/graphback-core/dist/index.mjs.map

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ export interface GraphbackCRUDGeneratorConfig {
subUpdate?: boolean
// Generate subscription for delete operation
subDelete?: boolean
unique?: string[]
}
2 changes: 2 additions & 0 deletions packages/graphback-core/src/plugin/GraphbackCoreMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export class GraphbackCoreMetadata {
let crudOptions = parseMetadata('model', modelType)
// Merge CRUD options from type with global ones
crudOptions = Object.assign({}, this.supportedCrudMethods, crudOptions)
const uniqueFields = Array.isArray(crudOptions.unique) ? crudOptions.unique : []
const { type: primaryKeyType, name } = getPrimaryKey(modelType)
const primaryKey = {
name,
Expand Down Expand Up @@ -145,6 +146,7 @@ export class GraphbackCoreMetadata {
primaryKey,
crudOptions,
relationships,
uniqueFields,
graphqlType: modelType
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/graphback-core/src/plugin/ModelDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface ModelDefinition {
graphqlType: GraphQLObjectType
relationships: FieldRelationshipMetadata[]
crudOptions: GraphbackCRUDGeneratorConfig
uniqueFields: string[]
}

export function getModelByName (name: string, models: ModelDefinition[]): ModelDefinition | undefined {
Expand Down
26 changes: 15 additions & 11 deletions packages/graphback-core/src/runtime/CRUDService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,14 @@ export class CRUDService<Type = any> implements GraphbackCRUDService<Type> {
this.pubSub = config.pubSub
}

public async create (data: Type, context?: GraphbackContext, info?: GraphQLResolveInfo): Promise<Type> {
public async initializeUniqueIndex() {
return await this.db.initializeUniqueIndex(this.model.uniqueFields)
}

public async create (data: Type, context?: GraphbackContext, info?: GraphQLResolveInfo, uniqueFields: string[]): Promise<Type> {
const [selectedFields, _] = getSelectedFieldsFromResolverInfo(info, this.model, true)

const result = await this.db.create(data, selectedFields)
const result = await this.db.create(data, selectedFields, uniqueFields)

if (this.pubSub && this.crudOptions.subCreate) {
const topic = this.subscriptionTopicMapping(GraphbackOperationType.CREATE, this.model.graphqlType.name)
Expand All @@ -61,10 +65,10 @@ export class CRUDService<Type = any> implements GraphbackCRUDService<Type> {
return result
}

public async update (data: Type, context?: GraphbackContext, info?: GraphQLResolveInfo): Promise<Type> {
public async update (data: Type, context?: GraphbackContext, info?: GraphQLResolveInfo, uniqueFields: string[]): Promise<Type> {
const [selectedFields, _] = getSelectedFieldsFromResolverInfo(info, this.model, true)

const result = await this.db.update(data, selectedFields)
const result = await this.db.update(data, selectedFields, uniqueFields)

if (this.pubSub && this.crudOptions.subUpdate) {
const topic = this.subscriptionTopicMapping(GraphbackOperationType.UPDATE, this.model.graphqlType.name)
Expand All @@ -79,18 +83,18 @@ export class CRUDService<Type = any> implements GraphbackCRUDService<Type> {
return result
}

public async updateBy (args: Partial<Type>, context?: GraphbackContext, info?: GraphQLResolveInfo): Promise<ResultList<Type>> {
public async updateBy (args: Partial<Type>, context?: GraphbackContext, info?: GraphQLResolveInfo, uniqueFields: string[]): Promise<ResultList<Type>> {
const [selectedFields, _] = getSelectedFieldsFromResolverInfo(info, this.mode, true)
const result = await this.db.updateBy(args, selectedFields)
const result = await this.db.updateBy(args, selectedFields, uniqueFields)

return {
items: result
}
}

public async delete (args: Partial<Type>, context?: GraphbackContext, info?: GraphQLResolveInfo): Promise<Type> {
public async delete (args: Partial<Type>, context?: GraphbackContext, info?: GraphQLResolveInfo, uniqueFields: string[]): Promise<Type> {
const [selectedFields, _] = getSelectedFieldsFromResolverInfo(info, this.model, true)
const result = await this.db.delete(data, selectedFields)
const result = await this.db.delete(data, selectedFields, uniqueFields)

if (this.pubSub && this.crudOptions.subDelete) {
const topic = this.subscriptionTopicMapping(GraphbackOperationType.DELETE, this.model.graphqlType.name)
Expand All @@ -105,9 +109,9 @@ export class CRUDService<Type = any> implements GraphbackCRUDService<Type> {
return result
}

public async deleteBy (args: Partial<Type>, context?: GraphbackContext, info?: GraphQLResolveInfo): Promise<ResultList<Type>> {
public async deleteBy (args: Partial<Type>, context?: GraphbackContext, info?: GraphQLResolveInfo, uniqueFields: string[]): Promise<ResultList<Type>> {
const [selectedFields, _] = getSelectedFieldsFromResolverInfo(info, this.model, true)
const result = await this.db.deleteBy(args, selectedFields)
const result = await this.db.deleteBy(args, selectedFields, uniqueFields)

return {
items: result
Expand All @@ -120,7 +124,7 @@ export class CRUDService<Type = any> implements GraphbackCRUDService<Type> {
}

public async findBy (args?: FindByArgs, context?: GraphbackContext, info?: GraphQLResolveInfo, path?: string): Promise<ResultList<Type>> {
let requestedCount: boolean = false
let requestedCount = false
const [selectedFields, fieldArgs] = getSelectedFieldsFromResolverInfo(info, this.model, false, path)
requestedCount = path === 'items' && getResolverInfoFieldsList(info).some((field: string) => field === 'count')
const items: Type[] = await this.db.findBy(args, selectedFields, fieldArgs)
Expand Down
10 changes: 5 additions & 5 deletions packages/graphback-core/src/runtime/GraphbackCRUDService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,23 @@ export interface GraphbackCRUDService<Type = any, GraphbackContext = any> {
* @param data input data
* @param context context object passed from graphql or rest layer
*/
create: (data: Type, context?: GraphbackContext, info?: GraphQLResolveInfo) => Promise<Type>
create: (data: Type, context?: GraphbackContext, info?: GraphQLResolveInfo, uniqueFields: string[]) => Promise<Type>

/**
* Implementation for object updates
*
* @param data input data including id
* @param context context object passed from graphql or rest layer
*/
update: (data: Partial<Type>, context?: GraphbackContext, info?: GraphQLResolveInfo) => Promise<Type>
update: (data: Partial<Type>, context?: GraphbackContext, info?: GraphQLResolveInfo, uniqueFields: string[]) => Promise<Type>

/**
* Implementation for object deletes
*
* @param data data used for consistency reasons
* @param context context object passed from graphql or rest layer
*/
delete: (data: Partial<Type>, context?: GraphbackContext, info?: GraphQLResolveInfo) => Promise<Type>
delete: (data: Partial<Type>, context?: GraphbackContext, info?: GraphQLResolveInfo, uniqueFields: string[]) => Promise<Type>

/**
* Fetch a single record by its unique attribute(s)
Expand Down Expand Up @@ -74,14 +74,14 @@ export interface GraphbackCRUDService<Type = any, GraphbackContext = any> {
* @param data input data including id
* @param context context object passed from graphql or rest layer
*/
updateBy: (args: Partial<Type>, context?: GraphbackContext, info?: GraphQLResolveInfo) => Promise<ResultList<Type>>
updateBy: (args: Partial<Type>, context?: GraphbackContext, info?: GraphQLResolveInfo, uniqueFields: string[]) => Promise<ResultList<Type>>
/**
* Implementation for object deletes by filter
*
* @param data data used for consistency reasons
* @param context context object passed from graphql or rest layer
*/
deleteBy: (args: Partial<Type>, context?: GraphbackContext, info?: GraphQLResolveInfo) => Promise<ResultList<Type>>
deleteBy: (args: Partial<Type>, context?: GraphbackContext, info?: GraphQLResolveInfo, uniqueFields: string[]) => Promise<ResultList<Type>>

/**
* Subscription for all creation events
Expand Down

0 comments on commit ece0cc2

Please sign in to comment.