The GraphQL Livebase API allows you to write objects to the database using mutation type operations. All CRUD scenarios are supported:
- Create new objects (Create);
- Update existing objects (Update);
- Make-a-generic-write (Create or Update);
- Delete objects (Delete).
In addition to the actual writing services, which as already stated are of type mutation (see conventions on service nomenclature), there are two types of services, of type query, which cover two “cross-cutting” scenarios, both of which support writing:
- Validate: services that allow simulating a write by checking its correctness at the Data Validation level, requesting any Issue of this type, but without raising server-side errors. More information is available at the bottom:
type ValidationResult
. - Preview: services that allow you to get a preview of the write result, which is useful for getting information computed by the server, such as derived attributes and associable objects.
Write services return as output the type <ClassName>
and type <ClassName>Page
structures, described for read services. Unlike the latter, the input structures used in writes are multiple and highly specialized. For this reason, in the following paragraphs, we have grouped input, writing services, and support services according to the scenario they are part of, among those listed above.
Reference engine model #
All examples on this page reference the engine model in the figure:
The Employee class has a role for each supported relationship type, as well as native, query, math, and platform attributes declared on it.
Create new objects #
The following services and facilities allow you to create new objects or test their creation.
create
service #
The Mutation.<ClassName>___create
service allows you to create a graph of objects from a certain class.
type Mutation {
ClassName___create(data: ClassNameCreate!, forceWarnings: ForceWarnings): ClassName
}
The service requests the structure <ClassName>Create
as input, and returns the structure <ClassName>
containing the newly created object as output. You can work around any warnings returned by the server caused by non-blocking Class Warnings by including the forceWarnings
argument, of type ForceWarnings
.
For example, the service to create an Employee object is as follows:
type Mutation {
Employee___create(data: EmployeeCreate!, forceWarnings: ForceWarnings): Employee
}
previewCreate
service #
The Query.<ClassName>___previewCreate
service allows you to get a preview of the result of creating a graph of objects, which is useful for getting information computed by the server, such as derived attributes and objects that can be associated with the graph.
type Query {
ClassName___previewCreate(data: ClassNameDraftCreate!): ClassName
}
The service requests as input the structure <ClassName>DraftCreate
, and returns as output the structure <ClassName>
, whose computed fields are pre-populated, if possible, from the information sent to the server; associables are always available for all roles.
For example, the service for getting a preview of an Employee object is as follows:
type Query {
Employee___previewCreate(data: EmployeeDraftCreate!): Employee
}
validateCreate
service #
The Query.<ClassName>___validateCreate
service mirrors the create
service, but with the purpose of checking whether the graph of objects created is correct at the Data Validation level.
type Query {
ClassName___validateCreate(data: ClassNameDraftCreate!): ValidationResult
}
The service requests the structure <ClassName>DraftCreate
(the same as the service previewCreate
) as input, and returns the structure ValidationResult
as output, which indicates whether the graph is valid or not and reports any Issues
at the Data Validation level.
For example, the service to validate the creation of an Employee object is as follows:
type Query {
Employee___validateCreate(data: EmployeeDraftCreate!): ValidationResult
}
input <ClassName>Create
. #
The input type <ClassName>Create
allows you to specify the data needed to create an object of class <ClassName>
. This input follows the structure type <ClassName>
, with some important differences.
Attributes are mapped as follows:
- only fields related to native (editable) attributes are present, while derived attributes and platform are excluded;
- fields related to required attributes are mandatory (
!
).
For example, for the Employee class, we have the following EmployeeCreate
structure:
input EmployeeCreate {
# attributes
first_name: String! # is required
last_name: String! # is required
date_of_birth: Date! # is required
email_address: String
date_joined: Date
hourly_cost: Real
username: String
# outgoing roles
# ...
}
The outgoing roles are mapped as follows:
- to-one associations:
ID
; - to-many associations:
[ID]
; - part to-one:
<PartClassName>Create
(analogous to<ClassName>Create
); - part to-many:
[<PartClassName>Create]
(analogous to[<ClassName>Create]
).
IDs specified for associations must refer to pre-existing instances for the pointed classes. On the other hand, part roles allow the contextual creation of a part object through its input (this is why we speak of the creation of an object graph).
For Employee, outgoing roles are mapped as follows:
input EmployeeCreate {
# attributes
# ...
# outgoing roles
team: ID # association to 1
supervisor: ID # reflexive association to 1
address: AddressCreate # composition to 1
qualification_: [ID] # association to N
assignments: [Project_assignmentCreate] # composition to N
}
input <ClassName>DraftCreate
. #
The input type <ClassName>Create
allows you to specify some data required to create an object of class <ClassName>
; it is the permissive counterpart of <ClassName>Create
, where no fields are required (!
). This allows more freedom to the services that use it (previewCreate
and validateCreate
).
For example, for the Employee class, you have the following EmployeeDraftCreate
structure:
input EmployeeCreate {
# attributes
first_name: String
last_name: String
date_of_birth: Date
email_address: String
date_joined: Date
hourly_cost: Real
username: String
# outgoing roles
team: ID # association to 1
supervisor: ID # reflexive association to 1
address: AddressCreate # composition to 1
qualification_: [ID] # association to N
assignments: [Project_assignmentCreate] # composition to N
}
Update existing objects #
The following services and facilities allow you to update pre-existing objects or test their modification.
update
service #
The Mutation.<ClassName>___update
service allows you to update one or more objects from a certain class via a modification graph.
type Mutation {
ClassName___update(data: ClassNameUpdate!, forceWarnings: ForceWarnings): ClassName
}
The service requests as input the structure <ClassName>Update
, and returns as output the structure <ClassName>
containing the newly updated object. You can work around any warnings returned by the server caused by non-blocking Class Warnings by including the forceWarnings
argument, of type ForceWarnings
.
For example, the service to update an Employee object is as follows:
type Mutation {
Employee___update(data: EmployeeUpdate!, forceWarnings: ForceWarnings): Employee
}
An example mutation with this service is available here.
previewUpdate
service #
The Query.<ClassName>___previewUpdate
service allows you to get a preview of the result of updating an graph of objects, which is useful for getting information computed by the server, such as derived attributes and objects that can be associated with the graph.
type Query {
ClassName___previewUpdate(data: ClassNameDraftUpdate!): ClassName
}
The service requests the structure <ClassName>DraftUpdate
as input, and returns the structure <ClassName>
as output, whose computed fields are pre-populated, if possible, from the information sent to the server; associables are always available for all roles.
For example, the service for getting a preview of an updated Employee object is as follows:
type Query {
Employee___previewUpdate(data: EmployeeDraftUpdate!): Employee
}
validateUpdate
service #
The Query.<ClassName>___validateUpdate
service mirrors the update
service, but with the purpose of checking whether the graph of changes is correct at the Data Validation level.
type Query {
ClassName___validateUpdate(data: ClassNameDraftUpdate!): ValidationResult
}
The service requests as input the structure <ClassName>DraftUpdate
(the same as the service previewUpdate
and returns as output the structure ValidationResult
, which indicates whether the graph is valid or not and reports any Issues
at the Data Validation level.
For example, the service to validate the update of a pre-existing Employee object is as follows:
type Query {
Employee___validateUpdate(data: EmployeeDraftUpdate!): ValidationResult
}
input <ClassName>Update
. #
The input type <ClassName>Update
allows you to specify the data required to modify a pre-existing object of class <ClassName>
. This input follows the type <ClassName>
structure, with some important differences.
Attributes are mapped as follows:
- only fields related to native (editable) attributes are present, while derived attributes and platform are excluded, except for the
_id: ID!
field; - unlike
<ClassName>Create
, fields related to required attributes are not mandatory; - the only mandatory field is
_id
, necessary to specify which class instance you want to modify.
For example, for the class Employee, you have the following structure EmployeeUpdate
:
input EmployeeUpdate {
_id: ID!
# attributes
first_name: String
last_name: String
date_of_birth: Date
email_address: String
date_joined: Date
hourly_cost: Real
username: String
# outgoing roles
# ...
}
The outgoing roles are mapped as follows:
- to-one associations:
<TargetClassName>RoleRef
(analogous to<ClassName>RoleRef
); - associations to many:
<TargetClassName>RoleRefs
(analogous to<ClassName>RoleRefs
); - part to one:
<PartClassName>RoleObject
(analogous to<ClassName>RoleObject
); - part to-many:
<PartClassName>RoleObjects
(analogous to<ClassName>RoleObjects
).
For Employee, the outgoing roles are mapped as follows:
input EmployeeUpdate {
# attributes
# ...
# outgoing roles
team: TeamRoleRef # association to 1
supervisor: EmployeeRoleRef # reflexive association to 1
address: AddressRoleObject # composition to 1
qualification_: QualificationRoleRefs # association to N
assignments: Project_assignmentRoleObjects # composition to N
}
input <ClassName>RoleRef(s)
#
The input type <ClassName>RoleRef
and <ClassName>RoleRefs
are intended to handle the modification of the association towards the class <ClassName>
in the context of the update
service. They are generated for all classes that have at least one incoming managed association_; as the name suggests, <ClassName>RoleRef
is generated for to-one associations, while <ClassName>RoleRefs
is generated for to-many associations.
input ClassNameRoleRef {
set: ID
remove: Boolean
}
input ClassNameRoleRefs {
add: [ID]
remove: [ID]
removeAll: Boolean
}
Contextually with the modification of an object of the Source class, it is possible to specify, for fields related to outgoing association roles, which objects of the corresponding Target classes you want to associate or remove:
<ClassName>RoleRef
allows you to associate with an existing object (by specifying its ID usingset
), or to remove the pre-existing association (by setting the valueremove: true
);<ClassName>RoleRefs
allows you to specify new associations (specifying a list of IDs usingadd
) and the removal of pre-existing associations (specifying a list of IDs usingremove
) at the same time, or allows you to remove all pre-existing associations (settingremoveAll: true
).
As shown above, for the Team and Qualification classes, the TeamRoleRef
and QualificationRoleRefs
inputs are generated, respectively; these appear in the EmployeeUpdate
input, in the team
(to one), and qualification_
(to many) role fields, respectively, as part of the Mutation.Employee___update
service.
Similarly, looking at the reference engine model, we can infer that the inputs ProjectRoleRef
and EmployeeRoleRef
(also the latter used by EmployeeUpdate
) appear in the generated GraphQL schema.
input <ClassName>RoleObject(s)
#
The input type <ClassName>RoleObject
and <ClassName>RoleObjects
are intended to handle the creation/modification of Part objects of class <ClassName>
, in the context of the update
service. They are generated for all classes that have at least one incoming Part role managed; as the name suggests, <ClassName>RoleObject
is generated for one compositions, while <ClassName>RoleRefs
is generated for many compositions.
input ClassNameRoleObject {
create: ClassNameCreate
update: ClassNameUpdate
delete: Boolean
}
input ClassNameRoleObjects {
create: [ClassNameCreate].
update: [ClassNameUpdate]
delete: [ID]
deleteAll: Boolean
}
While editing an object of class Main, it is possible to specify, for fields related to outgoing composition roles, which objects of the corresponding Part classes you want to create, modify or destroy:
<ClassName>RoleObject
allows you to create a new part object (by populating thecreate
field, of type<ClassName>Create
), update the pre-existing part object (by populating theupdate
field, of type<ClassName>Update
), or delete the pre-existing part object (by valorizingdelete: true
) ;<ClassName>RoleObjects
allows you to simultaneously specify the contextual creation of new part objects (by specifying a list of<ClassName>Create
viaadd
), the contextual modification of existing part objects (specifying a list of<ClassName>Update
viaupdate
), the contextual deletion of pre-existing part objects (specifying a list of IDs viadelete
), or allows the deletion of all pre-existing part objects (by settingdeleteAll: true
).
As shown above, for the Address and Project_assignment classes, the AddressRoleObject
and Project_assignmentRoleObjects
inputs are generated, respectively; these appear in the EmployeeUpdate
input, in the address
(to one) and assignments
(to many) role fields, respectively, as part of the Mutation.Employee___update
service.
input <ClassName>DraftUpdate
#
The input type <ClassName>DraftUpdate
is the variant of <ClassName>Update
used by the services previewUpdate
and validateUpdate
. It has no substantial differences with its counterpart, except for the structures used as the value of fields related to exiting part roles.
For example, for the Employee class, you have the following structure EmployeeDraftUpdate
:
input EmployeeDraftUpdate {
_id: ID! # same as EmployeeUpdate
# attributes (same as EmployeeUpdate)
first_name: String
last_name: String
date_of_birth: Date
email_address: String
date_joined: Date
hourly_cost: Real
username: String
# outgoing roles
#...
}
The outgoing roles are instead mapped as follows:
- to-one associations:
<TargetClassName>DraftUpdateRoleRef
(analogous to<ClassName>DraftUpdateRoleRef
); - associations to many:
<TargetClassName>RoleRefs
(analogous to<ClassName>DraftUpdateRoleRefs
); - part to one:
<PartClassName>DraftUpdateRoleObject
(analogous to<ClassName>DraftUpdateRoleObject
); - part to-many:
<PartClassName>DraftUpdateRoleObjects
(analogous to<ClassName>DraftUpdateRoleObjects
).
For Employee, the outgoing roles are mapped as follows:
input EmployeeDraftUpdate {
# attributes (same as EmployeeUpdate).
# ...
# outgoing roles
team: TeamDraftUpdateRoleRef # association to 1
supervisor: EmployeeDraftUpdateRoleRef # reflexive 1 association
address: AddressDraftUpdateRoleObject # 1-way composition
qualification_: QualificationDraftUpdateRoleRefs # association to N
assignments: Project_assignmentDraftUpdateRoleObjects # composition in N
}
input <ClassName>DraftUpdateRoleRef(s)
#
The input type <ClassName>DraftUpdateRoleRef
and <ClassName>DraftUpdateRoleRefs
are the variants of <ClassName>RoleRef
and [<ClassName>RoleRefs
](#input- classnamerolerefs) nested to the <ClassName>DraftUpdate
input used by the services previewUpdate
and validateUpdate
. Net of the different name, these structures coincide with their counterparts, as we can see from the following comparison:
input ClassNameDraftUpdateRoleRef {
set: ID
remove: Boolean
}
input ClassNameDraftUpdateRoleRefs {
add: [ID]
remove: [ID]
removeAll: Boolean
}
input ClassNameRoleRef {
set: ID
remove: Boolean
}
input ClassNameRoleRefs {
add: [ID]
remove: [ID]
removeAll: Boolean
}
input <ClassName>DraftUpdateRoleObject(s)
. #
The input type <ClassName>DraftUpdateRoleObject
and <ClassName>DraftUpdateRoleObjects
are the variants of <ClassName>RoleObject
and [<ClassName>RoleObjects
](#input- classnameroleobjects) nested to the <ClassName>DraftUpdate
input used by services previewUpdate
and validateUpdate
.
Compared to the association role structures, these also differ semantically from their counterparts. Since part object inputs allow contextual creation, DraftUpdate
and Update
inputs use <ClassName>DraftCreate
(which has no required fields) <ClassName>Create
(which can have required fields), respectively.
Below we see the comparison with <ClassName>RoleObject
/<ClassName>RoleObjects
:
input ClassNameDraftUpdateRoleObject {
create: ClassNameDraftCreate
update: ClassNameDraftUpdateUpdate
delete: Boolean
}
input ClassNameDraftUpdateRoleObjects {
create: [ClassNameDraftCreate]
update: [ClassNameDraftUpdateUpdate]
delete: [ID]
deleteAll: Boolean
}
input ClassNameRoleObject {
create: ClassNameCreate
update: ClassNameUpdate
delete: Boolean
}
input ClassNameRoleObjects {
create: [ClassNameCreate]
update: [ClassNameUpdate]
delete: [ID]
deleteAll: Boolean
}
This difference in the nested structures for the parts actually justifies having a dedicated DraftUpdate
structure for the previewUpdate
and validateUpdate
services to provide the same freedom as the previewCreate
and validateCreate
services.
updateBulk
service #
The Mutation.<ClassName>___updateBulk
service, the many counterpart of the [update
](#update service) service, allows one or more objects to be updated from a certain class via a modification graph that is applied in a bulk fashion to each selected object.
type Mutation {
ClassName___updateBulk(
options: ClassNamePageOptions!
data: ClassNameUpdateBulk!
forceWarnings: ForceWarnings
): ClassNameBulkResult
}
The service requires the structure <ClassName>PageOptions
as input, properly set to identify the objects you want to modify. Also required is an input <ClassName>UpdateBulk
, which describes the bulk changes that will be applied to each selected object.
You can work around any warnings returned by the server caused by non-blocking Class Warnings by including the forceWarnings
argument, of type ForceWarnings
.
The service returns a <ClassName>BulkResult
object as output, which contains, in the items
field, the list of objects modified by the mutation.
type ClassNameBulkResult {
items: [ClassName!]!
totalCount: Int!
}
You can specify which fields you want to retrieve for each record in the list, similar to the <ClassName>Page
structure. The totalCount
field shows the count of records
For example, the service for modifying several Employee objects is as follows:
type Mutation {
Employee___updateBulk(
options: EmployeePageOptions!
data: EmployeeUpdateBulk!
forceWarnings: ForceWarnings
): EmployeeBulkResult
}
validateUpdateBulk
service #
The Query.<ClassName>___validateUpdateBulk
service mirrors the updateBulk
service, but with the purpose of checking whether bulk modification of objects does not violate constraints at the Data Validation level.
type Query {
ClassName___validateUpdateBulk(options: ClassNamePageOptions!): ValidationResult
}
The service requests as input the structure <ClassName>DraftUpdateBulk
and returns as output the structure ValidationResult
, which indicates whether the bulk change is valid or not and reports any Issue
at the Data Validation level.
For example, the service to validate the bulk modification of several Employee objects is as follows:
type Query {
Employee___validateUpdateBulk(options: ClassNamePageOptions!): ValidationResult
}
input <ClassName>UpdateBulk
. #
The input type <ClassName>UpdateBulk
allows you to specify the data required for the bulk modification of pre-existing objects of class <ClassName>
. This input traces the input <ClassName>Update
, with one difference: the _id
field is absent.
In fact, the change graph described by this structure does not refer to a pre-existing object of the class but rather represents the changes that will be applied in bulk fashion to all objects selected by the service updateBulk
.
For example, for the Employee class, you have the following structure EmployeeUpdateBulk
:
input EmployeeUpdateBulk {
# _id is absent
# attributes (same as EmployeeUpdate)
first_name: String
last_name: String
date_of_birth: Date
email_address: String
date_joined: Date
hourly_cost: Real
username: String
# outgoing roles (same as EmployeeUpdate)
team: TeamRoleRef # association to 1
supervisor: EmployeeRoleRef # reflexive association to 1
address: AddressRoleObject # composition to 1
qualification_: QualificationRoleRefs # association to N
assignments: Project_assignmentRoleObjects # composition in N
}
input <ClassName>DraftUpdateBulk
. #
The input type <ClassName>DraftUpdateBulk
is the variant of <ClassName>UpdateBulk
used by the validateUpdateBulk
service. It does not have substantial differences with its counterpart, except for the structures used to value the fields related to outgoing part roles. In fact, the same considerations made for the input <ClassName>DraftUpdate
apply.
For example, for class Employee, you have the following structure EmployeeDraftUpdateBulk
:
input EmployeeDraftUpdateBulk {
# _id is absent (same as EmployeeUpdateBulk)
# attributes (same as EmployeeDraftUpdate)
first_name: String
last_name: String
date_of_birth: Date
email_address: String
date_joined: Date
hourly_cost: Real
username: String
# outgoing roles (same as EmployeeDraftUpdate)
team: TeamDraftUpdateRoleRef # association to 1
supervisor: EmployeeDraftUpdateRoleRef # reflexive association to 1
address: AddressDraftUpdateRoleObject # 1-way composition
qualification_: QualificationDraftUpdateRoleRefs # association to N
assignments: Project_assignmentDraftUpdateRoleObjects # composition in N
}
Make a generic write #
As seen so far, the services and facilities offered by the schema to cover the Create and Update scenarios are essentially very similar to each other; this approach is in line with the predictability principle of the GraphQL schema.
When designing applications that use the GraphQL API intensively (such as a web client), however, it may not be necessary to distinguish between a Create and an Update write; in such cases, the rigid typing structures seen so far may be a limitation, since the developer is forced to handle these two scenarios separately.
For this reason, starting with Livebase version 5.8, the schema offers generic write services (and their supporting Preview and Validate services) that bring these differences together, which use flexible inputs that can take on the meaning of a Create or Update depending on the fields being valued.
save
service #
The Mutation.<ClassName>___create
service allows you to create a graph of objects of a certain class, or to update one or more objects of the same class, via a graph of changes. It groups the create
and update
services.
type Mutation {
ClassName___save(data: ClassNameDraft!, forceWarnings: ForceWarnings): ClassName
}
The service requests the structure <ClassName>Draft
as input, and returns the structure <ClassName>
containing the modified object as output. You can work around any warnings returned by the server caused by non-blocking Class Warnings by including the forceWarnings
argument, of type ForceWarnings
.
For example, the service to update an Employee object is as follows:
type Mutation {
Employee___save(data: EmployeeDraft!, forceWarnings: ForceWarnings): Employee
}
preview
service #
The Query.<ClassName>___previewCreate
service allows you to get a preview of the result of creating or updating an graph of objects, useful for getting server-computed information such as derived attributes and objects that can be associated with the graph; It groups the previewCreate
and previewUpdate
services.
type Query {
ClassName___preview(data: ClassNameDraft!): ClassName
}
The service requests as input the structure <ClassName>Draft
, and returns as output the structure <ClassName>
, whose computed fields are pre-populated, if possible, from the information sent to the server; associables are always available for all roles.
For example, the service for getting a preview of a created or updated Employee object is as follows:
type Query {
Employee___preview(data: EmployeeDraft!): Employee
}
validate
service #
The Query.<ClassName>___validate
service mirrors the save
service, but to verify whether the graph of objects created or modified is correct at the Data Validation level. It groups the validateCreate
and validateUpdate
services.
type Query {
ClassName___validate(data: ClassNameDraft!): ValidationResult
}
The service requests the structure <ClassName>Draft
as input, and returns the structure ValidationResult
as output, which indicates whether the graph is valid or not and reports any Issues
at the Data Validation level.
For example, the service to validate the creation or update of a pre-existing Employee object is as follows:
type Query {
Employee___validate(data: EmployeeDraft!): ValidationResult
}
input <ClassName>Draft
. #
The input type <ClassName>Draft
is the most flexible of the available input structures, and is used indiscriminately by the save
, preview
and validate
services.
As stated, here, this flexible input is able to take on the meaning of a Create or Update depending on the valued fields.
Attributes are mapped as follows:
- only fields related to native (editable) attributes are present, while derived attributes and platform are excluded, except for the
_id: ID!
field; - unlike
<ClassName>Create
, fields related to required attributes are not required; - unlike
<ClassName>Update
, the_id
field is also not required. The presence or absence of a value for_id
distinguishes whether the operation is logically a Create or an Update;
For example, for the Employee class, you have the following EmployeeDraft
structure:
input EmployeeUpdate {
_id: ID # is not mandatory
# attributes
first_name: String
last_name: String
date_of_birth: Date
email_address: String
date_joined: Date
hourly_cost: Real
username: String
# outgoing roles
# ...
}
The outgoing roles are mapped as follows:
- to-one associations:
ID
. In the context of an Update, you can remove the existing association by enhancing this field tonull
; - associations to many:
<TargetClassName>DraftRoleRefs
(analogous to<ClassName>DraftRoleRefs
; - part to one:
<PartClassName>Draft
(analogous to<ClassName>Create
). In the context of an Update, you can update an existing part object by including the_id
in the nested structure; you can also delete the existing part by setting this field tonull
; - part to-many:
<PartClassName>DraftRoleObjects
(analogous to<ClassName>DraftRoleObjects
).
For Employee, the outgoing roles are mapped as follows:
input EmployeeCreate {
# attributes
# ...
# outgoing roles
team: ID # association to 1
supervisor: ID # reflexive association to 1
address: AddressDraft # composition to 1
qualification_: QualificationDraftRoleRefs # association to N
assignments: Project_assignmentDraftRoleObjects # composition to N
}
input <ClassName>DraftRoleRefs
. #
The input type <ClassName>DraftRoleRefs
is intended to handle the modification of the association towards the class <ClassName>
in the context of the service save
. Like its counterpart <ClassName>RoleRefs
, it is generated for all classes that have at least one many_ incoming managed association. Net of the different name, this structure coincides with its counterpart, as we can see from the following comparison:
input ClassNameDraftRoleRefs {
add: [ID]
remove: [ID]
removeAll: Boolean
}
input ClassNameRoleRefs {
add: [ID]
remove: [ID]
removeAll: Boolean
}
input <ClassName>DraftRoleObjects
. #
The input type <ClassName>RoleObjects
is intended to handle the creation/modification of Part objects of class <ClassName>
, in the context of the save
service. Like its counterpart <ClassName>RoleObjects
, it is generated for all classes that have at least one many_ incoming managed Part role.
Unlike its counterpart, this structure offers a single save
field (containing a list of <ClassName>Draft
) that merges the create
and update
fields and allows the specification of both the creation of new part objects and the modification of existing part objects. The semantics of each operation is determined by the presence of a value in the nested _id
field of each part object.
Below we see the comparison with <ClassName>RoleObjects
:
input ClassNameDraftRoleObjects {
save: [ClassNameDraft]
delete: [ID]
deleteAll: Boolean
}
input ClassNameRoleObjects {
create: [ClassNameCreate]
update: [ClassNameUpdate]
delete: [ID]
deleteAll: Boolean
}
Delete objects #
The following services and facilities allow you to delete objects or test their deletion.
delete
service #
The Mutation.<ClassName>___delete
service allows you to delete an object of a certain class.
type Mutation {
ClassName___delete(_id: ID!, forceWarnings: ForceWarnings): DeleteResult
}
The service requests as input the ID of the object of <ClassName>
that you want to delete, and returns as output an object <DeleteResult>
; the latter simply contains the field deleted: Boolean
, which is worth true
if the object was deleted or false
otherwise (for example, if it was not found).
type DeleteResult {
deleted: Boolean
}
You can work around any warnings returned by the server caused by non-blocking Class Warnings by including the forceWarnings
argument, of type ForceWarnings
.
For example, the service to delete an Employee object is as follows:
type Mutation {
Employee___delete(_id: ID!, forceWarnings: ForceWarnings): DeleteResult
}
Which can be used as follows:
mutation {
Employee___delete(_id: "10101") {
deleted
}
}
{
"deleted": true
}
validateDelete
service #
The Query.<ClassName>___validateDelete
service mirrors the delete
service, but with the purpose of checking whether deleting the object does not violate constraints at the Data Validation level.
type Query {
ClassName___validateDelete(_id: ID!): ValidationResult
}
The service requests the ID of the object of <ClassName>
that you want to delete as input and returns the ValidationResult
structure as output, which indicates whether the deletion is valid or not and reports any Issue
at the Data Validation level.
For example, the service to validate the deletion of an Employee object is as follows:
type Query {
Employee___validateDelete(_id: ID!): ValidationResult
}
deleteBulk
service #
The Mutation.<ClassName>___deleteBulk
service, the to-many counterpart of the delete
service, allows you to delete one or more objects of a certain class.
type Mutation {
ClassName___deleteBulk(
options: ClassNamePageOptions!
forceWarnings: ForceWarnings
): DeleteBulkResult
}
The service requires as input the structure <ClassName>PageOptions
, properly valorized to identify the set of objects you want to delete, and returns in output an object <DeleteBulkResult>
; the latter simply contains the field deleted: Int
, to indicate the number of deleted objects.
type DeleteBulkResult {
deleted: Boolean
}
You can work around any warnings returned by the server caused by non-blocking Class Warnings by including the forceWarnings
argument, of type ForceWarnings
.
For example, the service to delete several Employee objects is as follows:
type Mutation {
Employee___deleteBulk(
options: EmployeePageOptions!
forceWarnings: ForceWarnings
): DeleteBulkResult
}
validateDeleteBulk
service #
The Query.<ClassName>___validateDeleteBulk
service mirrors the deleteBulk
service, but with the purpose of checking whether deleting objects does not violate constraints at the Data Validation level.
type Query {
ClassName___validateDeleteBulk(options: ClassNamePageOptions!): ValidationResult
}
The service requests the ID of the object of <ClassName>
that you want to delete as input and returns the ValidationResult
structure as output, which indicates whether the deletion is valid or not and reports any Issue
at the Data Validation level.
For example, the service to validate the deletion of several Employee objects is as follows:
type Query {
Employee___validateDeleteBulk(options: ClassNamePageOptions!): ValidationResult
}
type ValidationResult
#
All services of type Validate mentioned so far (such as Query.<ClassName>___validate
) return the following common ValidationResult
structure:
type ValidationResult {
isValid: Boolean
issues: [Issue]
}
The isValid
field is worth true
if, in checking the correctness of the call at the Data Validation level, the server raised Issues
of this type. The same Issues
are reported in the issues
field.
It is important to note that services of type Validate are the only ones able to get Issues
from the GraphQL server in the data
field and not nested in the errors
field; this is because, in communicating the Issues, the server does not go into exception. However, these services can only “detect” Issues
at the Data Validation level. More information is available here: Reference: Issue
types.
input ForceWarnings
. #
As stated in The GraphQL schema: Server-side errors, by default, the server returns a GraphQL error whether there are (blocking) errors or (non-blocking) warnings in the application, as is the case when a Class warning is set to not prevent the user from performing an action.
In GraphQL, some services allow you to “force” warnings not to block execution.
The mutation type write services listed are forceable via the forceWarnings
argument:
Mutation.<ClassName>___create
;Mutation.<ClassName>___update
;Mutation.<ClassName>___updateBulk
;Mutation.<ClassName>___save
;Mutation.<ClassName>___delete
;Mutation.<ClassName>___deleteBulk
.
The input type ForceWarnings
consists of two options:
input ForceWarnings {
actionVeto: Boolean = false
dataValidation: Boolean = false
}
These allow you to force warnings raised by the server in response to the events for which you can set a Class Warning.
Basically, actionVeto
refers to the following events:
- Edit: occurs when a GraphQL service that modifies existing data (
update
,save
,delete
, …) is invoked; - Create: occurs when a GraphQL service that creates new data (
create
,save
) is invoked.
On the other hand, dataValidation
refers to the Database events in the figure.
By properly setting one of these two fields to true
, it is possible to bypass any non-blocking Class Warning defined on our class of interest.
Let’s look at an example. Suppose we have defined the following non-blocking Class Warning on Employee:
The following call fails:
mutation {
Employee___save(
data: {
first_name: "Reiner"
last_name: "Galliard"
date_of_birth: "15/08/1990"
date_joined: "10/10/2018"
hourly_cost: "12.0"
}
) {
full_name
age
}
}
{
"errors": [
{
"message": "Hourly cost is too low concerning the employee's experience.",
"extensions": {
"sourceRequestReference": "Employee___save",
"userMessage": "Hourly cost is too low with respect to the employee's experience.",
"issueLevel": "WARNING",
"issueReferenceType": "ENTITY",
"issueType": "ENTITY_DOMAIN",
"entityName": "Employee",
"entityID": "117010",
"_clientId": "",
"attributeNames": [],
"roleNames": [],
"applicationName": "Administration",
"profileName": "Administrator",
"traceId": "fd2eab81a5ea1ec2c6c7687ad5d3bf080e14c130",
"classification": "DataFetchingException"
}
}
],
"data": {
"Employee___save": null
}
}
The server returned a single Issue
of level WARNING
. Instead of changing the value of the hourly_cost
field, we modify the call so that it forces the Class warning and verify that the next call is successful:
mutation {
Employee___save(
data: {
first_name: "Reiner"
last_name: "Galliard"
date_of_birth: "15/08/1990"
date_joined: "10/10/2018"
hourly_cost: "12"
}
forceWarnings: {dataValidation: true}
) {
full_name
age
}
}
{
"data": {
"Employee___save": {
"full_name": "Reiner Leonhart",
"age": 30
}
}
}