Skip to content

Batch Functionality #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"scripts": {
"prepublishOnly": "grunt default",
"build": "grunt default",
"test": "cd sample && npm start"
"test": "cd sample && npm start",
"retest": "npm run build && cd sample && npm run restart"
}
}
4 changes: 3 additions & 1 deletion sample/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
"sqlite3": "4.2.0"
},
"scripts": {
"start": "babel-node --presets @babel/preset-env --only sample.js sample.js"
"start": "babel-node --presets @babel/preset-env --only sample.js sample.js",
"clean": "shx rm -rf node_modules/graphql-tools-sequelize",
"restart": "npm run clean && npm i graphql-tools-sequelize && npm run start"
}
}
164 changes: 162 additions & 2 deletions sample/sample.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ import Sequelize from "sequelize"
${gts.entityCreateSchema("OrgUnit")}
${gts.entityUpdateSchema("OrgUnit")}
${gts.entityDeleteSchema("OrgUnit")}
${gts.entityBatchSchema("OrgUnit")}
}
type Person {
${gts.attrIdSchema("Person")}
Expand All @@ -143,6 +144,7 @@ import Sequelize from "sequelize"
${gts.entityCreateSchema("Person")}
${gts.entityUpdateSchema("Person")}
${gts.entityDeleteSchema("Person")}
${gts.entityBatchSchema("Person")}
}
enum Role {
principal
Expand Down Expand Up @@ -170,7 +172,8 @@ import Sequelize from "sequelize"
clone: gts.entityCloneResolver ("OrgUnit"),
create: gts.entityCreateResolver("OrgUnit"),
update: gts.entityUpdateResolver("OrgUnit"),
delete: gts.entityDeleteResolver("OrgUnit")
delete: gts.entityDeleteResolver("OrgUnit"),
batch: gts.entityBatchResolver("OrgUnit")
},
Person: {
id: gts.attrIdResolver ("Person"),
Expand All @@ -181,7 +184,8 @@ import Sequelize from "sequelize"
clone: gts.entityCloneResolver ("Person"),
create: gts.entityCreateResolver("Person"),
update: gts.entityUpdateResolver("Person"),
delete: gts.entityDeleteResolver("Person")
delete: gts.entityDeleteResolver("Person"),
batch: gts.entityBatchResolver("Person")
}
}

Expand Down Expand Up @@ -220,6 +224,7 @@ import Sequelize from "sequelize"
name: "CoC Web Technologies",
parentUnit: "${uXT.id}",
director: "acf34c80-9f83-11e6-8d46-080027e303e4"
members: "acf34c80-9f83-11e6-8d46-080027e303e4"
}
) {
id initials name
Expand All @@ -234,6 +239,13 @@ import Sequelize from "sequelize"
parentUnit { id name }
members { id name }
}
q2: Person(where: {
id: "acf34c80-9f83-11e6-8d46-080027e303e4"
}) {
id
name
belongsTo { id name }
}
u1: Person(id: "acf34c80-9f83-11e6-8d46-080027e303e4") {
update(with: { initials: "XXX", role: "assistant" }) {
id initials name role
Expand All @@ -244,6 +256,154 @@ import Sequelize from "sequelize"
id initials name
}
}
b1: OrgUnit {
batch (
collection: [ {
op: "CLONE",
type: "OrgUnit",
id: "acf34c80-9f83-11e6-8d47-080027e303e4"
} ]
) { id }
}
b2: OrgUnit {
batch (
collection: [ {
op: "CREATE",
type: "OrgUnit",
ref: "$orgId",
id: "48a44b20-f363-11ea-a870-d322e0e3adb0"
with: {
initials: "CoC-UX",
name: "CoC User Experience",
parentUnit: "${uXT.id}",
director: "acf34c80-9f83-11e6-8d46-080027e303e4"
}
}, {
op: "CREATE",
type: "Person",
id: "2e1579e0-f364-11ea-9e92-8bbb944fc462"
with: {
initials: "LZE",
name: "Linda Zeman",
supervisor: "${pRSE.id}",
belongsTo: "$orgId"
}
}, {
op: "CREATE",
type: "Person",
id: "1a74d8e0-f350-11ea-bede-335547113b7d"
with: {
initials: "TSC",
name: "Thomas Schöpf",
supervisor: "${pRSE.id}",
belongsTo: "$orgId"
}
}, {
op: "CREATE",
type: "Person",
with: {
initials: "EWA",
name: "Erwin Wacha",
supervisor: "${pRSE.id}",
belongsTo: "$orgId"
}
} ]
) {
id initials name parentUnit { id name } director { id name } members { id name }
}
}
b3: OrgUnit {
batch (
collection: [ {
op: "UPDATE",
type: "OrgUnit",
id: "acf34c80-9f83-11e6-8d47-080027e303e4",
with: {
initials: "CoC-WT2",
name: "CoC Web Technologies 2"
}
}, {
op: "UPDATE",
type: "OrgUnit",
id: "48a44b20-f363-11ea-a870-d322e0e3adb0",
root: true,
with: {
initials: "CoC-UX 2",
name: "CoC User Experience 2"
}
} ]
) { id initials name }
}
b4: OrgUnit(id: "48a44b20-f363-11ea-a870-d322e0e3adb0") {
batch (
collection: [ {
op: "UPDATE",
type: "OrgUnit",
id: "48a44b20-f363-11ea-a870-d322e0e3adb0",
with: {
id: "48a44b20-f363-11ea-a870-d322e0e3adb0",
initials: "CoC-AA",
name: "CoC Application Architecture",
}
}, {
op: "CREATE",
type: "Person",
with: {
initials: "LSC",
name: "Lisa Schötz",
supervisor: "${pRSE.id}",
belongsTo: "48a44b20-f363-11ea-a870-d322e0e3adb0"
}
}, {
op: "UPDATE",
type: "Person",
id: "2e1579e0-f364-11ea-9e92-8bbb944fc462",
with: { name: "Linda Elisabeth Zeman" }
}, {
op: "DELETE",
type: "Person",
id: "1a74d8e0-f350-11ea-bede-335547113b7d"
} ]
) {
id initials name members { id name }
}
}
b5: OrgUnit {
batch (
collection: [ {
op: "UPDATE",
type: "Person",
id: "2e1579e0-f364-11ea-9e92-8bbb944fc462",
with: { initials: "LTU" }
} ]
) { id }
}
b6: OrgUnit(id: "48a44b20-f363-11ea-a870-d322e0e3adb0") {
batch (
collection: [ {
op: "UPDATE",
type: "Person",
id: "2e1579e0-f364-11ea-9e92-8bbb944fc462",
with: { name: "Linda Elisabeth Turke" }
} ]
) {
id initials name members { id name }
}
}
b7: OrgUnit {
batch (
collection: [ {
op: "DELETE",
type: "OrgUnit",
id: "acf34c80-9f83-11e6-8d47-080027e303e4"
}, {
op: "DELETE",
type: "OrgUnit",
id: "48a44b20-f363-11ea-a870-d322e0e3adb0",
root: true
} ]
) { id }
}
d1: Person(id: "acf34c80-9f83-11e6-8d46-080027e303e4") {
delete
}
Expand Down
2 changes: 1 addition & 1 deletion src/gts-3-util-graphql.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default class gtsUtilGraphQL {
let type = fieldsAll[field].type
while (typeof type.ofType === "object")
type = type.ofType
if (field.match(/^(?:clone|create|update|delete)$/))
if (field.match(/^(?:clone|create|update|delete|batch)$/))
fields.method[field] = type.name
else if ( type.constructor.name === "GraphQLScalarType"
|| type.constructor.name === "GraphQLEnumType" )
Expand Down
140 changes: 73 additions & 67 deletions src/gts-8-entity-create.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,77 +38,83 @@ export default class gtsEntityCreate {
if (!(typeof entity === "object" && entity instanceof this._anonCtx && entity.isType(type)))
throw new Error(`method "create" only allowed in anonymous ${type} context`)

/* determine fields of entity as defined in GraphQL schema */
const defined = this._fieldsOfGraphQLType(info, type)

/* determine fields of entity as requested in GraphQL request */
const build = this._fieldsOfGraphQLRequest(args, info, type)

/* handle unique id */
if (args[this._idname] === undefined)
/* auto-generate the id */
build.attribute[this._idname] = this._idmake()
else {
/* take over id, but ensure it is unique */
build.attribute[this._idname] = args[this._idname]
const opts = {}
if (ctx.tx !== undefined)
opts.transaction = ctx.tx
opts.attributes = [ this._idname ]
const existing = await this._models[type].findByPk(build.attribute[this._idname], opts)
if (existing !== null)
throw new Error(`entity ${type}#${build.attribute[this._idname]} already exists`)
}

/* validate attributes */
await this._validate(type, build, ctx)

/* build a new entity */
const obj = this._models[type].build(build.attribute)

/* check access to entity before action */
if (!(await this._authorized("before", "create", type, obj, ctx)))
throw new Error(`will not be allowed to create entity of type "${type}"`)

/* save new entity */
const opts = {}
if (ctx.tx !== undefined)
opts.transaction = ctx.tx
const err = await obj.save(opts).catch((err) => err)
if (typeof err === "object" && err instanceof Error)
throw new Error("Sequelize: save: " + err.message + ":" +
err.errors.map((e) => e.message).join("; "))

/* post-adjust the relationships according to the request */
await this._entityUpdateFields(type, obj,
defined.relation, build.relation, ctx, info)

/* check access to entity after action */
if (!(await this._authorized("after", "create", type, obj, ctx)))
throw new Error(`was not allowed to create entity of type "${type}"`)

/* check access to entity again */
if (!(await this._authorized("after", "read", type, obj, ctx)))
throw new Error(`was not allowed to read (created) entity of type "${type}"`)

/* map field values */
this._mapFieldValues(type, obj, ctx, info)

/* update FTS index */
this._ftsUpdate(type, obj[this._idname], obj, "create")

/* trace access */
await this._trace({
op: "create",
arity: "one",
dstType: type,
dstIds: [ obj[this._idname] ],
dstAttrs: Object.keys(build.attribute).concat(Object.keys(build.relation))
}, ctx)
const obj = await this._entityCreate(type, entity, args, ctx, info)

/* return new entity */
return obj
}
}
/* argument args must be of type object and have attribute 'with' */
async _entityCreate (type, entity, args, ctx, info) {
/* determine fields of entity as defined in GraphQL schema */
const defined = this._fieldsOfGraphQLType(info, type)

/* determine fields of entity as requested in GraphQL request */
const build = this._fieldsOfGraphQLRequest(args, info, type)

/* handle unique id */
if (args[this._idname] === undefined)
/* auto-generate the id */
build.attribute[this._idname] = this._idmake()
else {
/* take over id, but ensure it is unique */
build.attribute[this._idname] = args[this._idname]
const opts = {}
if (ctx.tx !== undefined)
opts.transaction = ctx.tx
opts.attributes = [ this._idname ]
const existing = await this._models[type].findByPk(build.attribute[this._idname], opts)
if (existing !== null)
throw new Error(`entity ${type}#${build.attribute[this._idname]} already exists`)
}

/* validate attributes */
await this._validate(type, build, ctx)

/* build a new entity */
const obj = this._models[type].build(build.attribute)

/* check access to entity before action */
if (!(await this._authorized("before", "create", type, obj, ctx)))
throw new Error(`will not be allowed to create entity of type "${type}"`)

/* save new entity */
const opts = {}
if (ctx.tx !== undefined)
opts.transaction = ctx.tx
const err = await obj.save(opts).catch((err) => err)
if (typeof err === "object" && err instanceof Error)
throw new Error("Sequelize: save: " + err.message + ":" +
err.errors.map((e) => e.message).join("; "))

/* post-adjust the relationships according to the request */
await this._entityUpdateFields(type, obj,
defined.relation, build.relation, ctx, info)

/* check access to entity after action */
if (!(await this._authorized("after", "create", type, obj, ctx)))
throw new Error(`was not allowed to create entity of type "${type}"`)

/* check access to entity again */
if (!(await this._authorized("after", "read", type, obj, ctx)))
throw new Error(`was not allowed to read (created) entity of type "${type}"`)

/* map field values */
this._mapFieldValues(type, obj, ctx, info)

/* update FTS index */
this._ftsUpdate(type, obj[this._idname], obj, "create")

/* trace access */
await this._trace({
op: "create",
arity: "one",
dstType: type,
dstIds: [ obj[this._idname] ],
dstAttrs: Object.keys(build.attribute).concat(Object.keys(build.relation))
}, ctx)

return obj
}
}

Loading