La API GraphQL Livebase consente di scrivere oggetti sul database mediante operazioni di tipo mutation. Sono supportati tutti gli scenari CRUD:
- Creare nuovi oggetti (Create);
- Aggiornare oggetti esistenti (Update);
- Effettuare una scrittura generica (Create o Update);
- Eliminare oggetti (Delete).
Oltre ai servizi di scrittura veri e propri, che come giĂ affermato sono di tipo mutation (vedi convenzioni sulla nomenclatura dei servizi), esistono due tipologie di servizi, di tipo query, che coprono due scenari âtrasversaliâ, entrambi di supporto alle scritture:
- Validate: servizi che consentono di simulare una scrittura verificandone la correttezza a livello di Data Validation, richiedendo eventuali Issue di questo tipo, ma senza sollevare errori lato server. Maggiori informazioni sono disponibili in fondo:
type ValidationResult
. - Preview: servizi che consentono di ottenere unâanteprima del risultato della scrittura, utile per ottenere informazioni calcolate dal server, come attributi derivati e oggetti associabili.
I servizi di scrittura restituiscono in output le strutture type <ClassName>
e type <ClassName>Page
, descritte per i servizi di lettura. A differenza di questi ultimi, le strutture di input usate nelle scritture sono molteplici e altamente specializzate. Per questo motivo, nei seguenti paragrafi abbiamo raggruppato input, servizi di scrittura e servizi di supporto in base allo scenario di cui fanno parte, tra quelli elencati sopra.
Modello di riferimento #
Tutti gli esempi in questa pagina fanno riferimento al modello in figura:
La classe Employee ha un ruolo per ogni tipologia di relazione supportata, nonchĂŠ attributi nativi, query, math e platform dichiarati su essa.
Creare nuovi oggetti #
I seguenti servizi e strutture consentono di creare nuovi oggetti o testarne la creazione.
Servizio create
#
Il servizio Mutation.<ClassName>___create
consente di creare un grafo di oggetti a partire da una certa classe.
type Mutation {
ClassName___create(data: ClassNameCreate!, forceWarnings: ForceWarnings): ClassName
}
Il servizio richiede in input la struttura <ClassName>Create
, e restituisce in output la struttura <ClassName>
contenente lâoggetto appena creato. Ă possibile aggirare eventuali warning restituiti dal server causati da Class Warning non bloccanti includendo lâargomento forceWarnings
, di tipo ForceWarnings
.
Ad esempio, il servizio per creare un oggetto Employee è il seguente:
type Mutation {
Employee___create(data: EmployeeCreate!, forceWarnings: ForceWarnings): Employee
}
Servizio previewCreate
#
Il servizio Query.<ClassName>___previewCreate
consente di ottenere unâanteprima del risultato della creazione di un grafo di oggetti, utile per ottenere informazioni calcolate dal server, come attributi derivati e oggetti associabili al grafo.
type Query {
ClassName___previewCreate(data: ClassNameDraftCreate!): ClassName
}
Il servizio richiede in input la struttura <ClassName>DraftCreate
, e restituisce in output la struttura <ClassName>
, i cui campi calcolati sono pre-popolati, se possibile, a partire dalle informazioni inviate al server; gli associabili sono sempre disponibili per tutti i ruoli.
Ad esempio, il servizio per ottenere una preview di un oggetto Employee è il seguente:
type Query {
Employee___previewCreate(data: EmployeeDraftCreate!): Employee
}
Servizio validateCreate
#
Il servizio Query.<ClassName>___validateCreate
riflette il servizio create
, ma con lo scopo di verificare se il grafo di oggetti creato sia corretto a livello di Data Validation.
type Query {
ClassName___validateCreate(data: ClassNameDraftCreate!): ValidationResult
}
Il servizio richiede in input la struttura <ClassName>DraftCreate
(la stessa del servizio previewCreate
e restituisce in output la struttura ValidationResult
, che indica se il grafo è valido o meno e riporta eventuali Issue
a livello di Data Validation.
Ad esempio, il servizio per validare la creazione di un oggetto Employee è il seguente:
type Query {
Employee___validateCreate(data: EmployeeDraftCreate!): ValidationResult
}
input <ClassName>Create
#
Lâinput type <ClassName>Create
permette di specificare i dati necessari alla creazione di un oggetto della classe <ClassName>
. Questo input ricalca la struttura type <ClassName>
, con alcune importanti differenze.
Gli attributi vengono mappati come segue:
- sono presenti solamente campi relativi ad attributi nativi (editabili), mentre sono esclusi attributi derivati e platform;
- i campi relativi ad attributi required sono obbligatori (
!
).
Ad esempio, per la classe Employee, si ha la seguente struttura EmployeeCreate
:
input EmployeeCreate {
# attributi
first_name: String! # è required
last_name: String! # è required
date_of_birth: Date! # è required
email_address: String
date_joined: Date
hourly_cost: Real
username: String
# ruoli uscenti
# ...
}
I ruoli uscenti vengono mappati come segue:
- associazioni a uno:
ID
; - associazioni a molti:
[ID]
; - part a uno:
<PartClassName>Create
(analogo a<ClassName>Create
); - part a molti:
[<PartClassName>Create]
(analogo a[<ClassName>Create]
).
Gli ID specificati per le associazioni devono far riferimento a istanze pre-esistenti per le classi puntate. I ruoli part consentono invece la creazione contestuale di un oggetto part attraverso il relativo input (per questo motivo si parla di creazione di un grafo di oggetti).
Per Employee, i ruoli uscenti vengono mappati come segue:
input EmployeeCreate {
# attributi
# ...
# ruoli uscenti
team: ID # associazione a 1
supervisor: ID # associazione riflessiva a 1
address: AddressCreate # composizione a 1
qualification_: [ID] # associazione a N
assignments: [Project_assignmentCreate] # composizione a N
}
input <ClassName>DraftCreate
#
Lâinput type <ClassName>Create
permette di specificare alcuni dati necessari alla creazione di un oggetto della classe <ClassName>
; si tratta della controparte permissiva di <ClassName>Create
, in cui nessun campo è required (!
). Questo consente maggiore libertĂ ai servizi che la utilizzano (previewCreate
e validateCreate
).
Ad esempio, per la classe Employee, si ha la seguente struttura EmployeeDraftCreate
:
input EmployeeCreate {
# attributi
first_name: String
last_name: String
date_of_birth: Date
email_address: String
date_joined: Date
hourly_cost: Real
username: String
# ruoli uscenti
team: ID # associazione a 1
supervisor: ID # associazione riflessiva a 1
address: AddressCreate # composizione a 1
qualification_: [ID] # associazione a N
assignments: [Project_assignmentCreate] # composizione a N
}
Aggiornare oggetti esistenti #
I seguenti servizi e strutture consentono di aggiornare oggetti pre-esistenti o testarne la modifica.
Servizio update
#
Il servizio Mutation.<ClassName>___update
consente di aggiornare uno o piĂš oggetti a partire da una certa classe, tramite un grafo di modifiche.
type Mutation {
ClassName___update(data: ClassNameUpdate!, forceWarnings: ForceWarnings): ClassName
}
Il servizio richiede in input la struttura <ClassName>Update
, e restituisce in output la struttura <ClassName>
contenente lâoggetto appena aggiornato. Ă possibile aggirare eventuali warning restituiti dal server causati da Class Warning non bloccanti includendo lâargomento forceWarnings
, di tipo ForceWarnings
.
Ad esempio, il servizio per aggiornare un oggetto Employee è il seguente:
type Mutation {
Employee___update(data: EmployeeUpdate!, forceWarnings: ForceWarnings): Employee
}
Un esempio di mutation con questo servizio è disponibile qui.
Servizio previewUpdate
#
Il servizio Query.<ClassName>___previewUpdate
consente di ottenere unâanteprima del risultato dellâaggiornamento di un grafo di oggetti, utile per ottenere informazioni calcolate dal server, come attributi derivati e oggetti associabili al grafo.
type Query {
ClassName___previewUpdate(data: ClassNameDraftUpdate!): ClassName
}
Il servizio richiede in input la struttura <ClassName>DraftUpdate
, e restituisce in output la struttura <ClassName>
, i cui campi calcolati sono pre-popolati, se possibile, a partire dalle informazioni inviate al server; gli associabili sono sempre disponibili per tutti i ruoli.
Ad esempio, il servizio per ottenere una preview di un oggetto Employee aggiornato è il seguente:
type Query {
Employee___previewUpdate(data: EmployeeDraftUpdate!): Employee
}
Servizio validateUpdate
#
Il servizio Query.<ClassName>___validateUpdate
riflette il servizio update
, ma con lo scopo di verificare se il grafo di modifiche sia corretto a livello di Data Validation.
type Query {
ClassName___validateUpdate(data: ClassNameDraftUpdate!): ValidationResult
}
Il servizio richiede in input la struttura <ClassName>DraftUpdate
(la stessa del servizio previewUpdate
e restituisce in output la struttura ValidationResult
, che indica se il grafo è valido o meno e riporta eventuali Issue
a livello di Data Validation.
Ad esempio, il servizio per validare lâaggiornamento di un oggetto Employee pre-esistente è il seguente:
type Query {
Employee___validateUpdate(data: EmployeeDraftUpdate!): ValidationResult
}
input <ClassName>Update
#
Lâinput type <ClassName>Update
permette di specificare i dati necessari alla modifica di un oggetto pre-esistente della classe <ClassName>
. Questo input ricalca la struttura type <ClassName>
, con alcune importanti differenze.
Gli attributi vengono mappati come segue:
- sono presenti solamente campi relativi ad attributi nativi (editabili), mentre sono esclusi attributi derivati e platform, a eccezione del campo
_id: ID!
; - a differenza di
<ClassName>Create
, i campi relativi ad attributi required non sono obbligatori; - lâunico campo obbligatorio è
_id
, necessario infatti a specificare quale istanza della classe si vuole modificare.
Ad esempio, per la classe Employee, si ha la seguente struttura EmployeeUpdate
:
input EmployeeUpdate {
_id: ID!
# attributi
first_name: String
last_name: String
date_of_birth: Date
email_address: String
date_joined: Date
hourly_cost: Real
username: String
# ruoli uscenti
# ...
}
I ruoli uscenti vengono mappati come segue:
- associazioni a uno:
<TargetClassName>RoleRef
(analogo a<ClassName>RoleRef
); - associazioni a molti:
<TargetClassName>RoleRefs
(analogo a<ClassName>RoleRefs
); - part a uno:
<PartClassName>RoleObject
(analogo a<ClassName>RoleObject
); - part a molti:
<PartClassName>RoleObjects
(analogo a<ClassName>RoleObjects
).
Per Employee, i ruoli uscenti vengono mappati come segue:
input EmployeeUpdate {
# attributi
# ...
# ruoli uscenti
team: TeamRoleRef # associazione a 1
supervisor: EmployeeRoleRef # associazione riflessiva a 1
address: AddressRoleObject # composizione a 1
qualification_: QualificationRoleRefs # associazione a N
assignments: Project_assignmentRoleObjects # composizione a N
}
input <ClassName>RoleRef(s)
#
Gli input type <ClassName>RoleRef
e <ClassName>RoleRefs
hanno lo scopo di gestire la modifica dellâassociazione verso la classe <ClassName>
nel contesto del servizio update
. Vengono generati per tutte le classi che hanno almeno unâassociazione entrante managed; come suggerisce il nome, <ClassName>RoleRef
viene generato per associazioni a uno, mentre <ClassName>RoleRefs
viene generato per associazioni a molti.
input ClassNameRoleRef {
set: ID
remove: Boolean
}
input ClassNameRoleRefs {
add: [ID]
remove: [ID]
removeAll: Boolean
}
Contestualmente alla modifica di un oggetto della classe Source, è possibile specificare, per campi relativi ai ruoli di associazione uscenti, quali oggetti delle corrispondenti classi Target si vogliono associare o rimuovere:
<ClassName>RoleRef
consente di associarsi a un oggetto esistente (specificandone lâID medianteset
), oppure di rimuovere lâassociazione pre-esistente (valorizzandoremove: true
);<ClassName>RoleRefs
consente di specificare contemporaneamente nuove associazioni (specificando una lista di ID medianteadd
) e la rimozione di associazioni pre-esistenti (specificando una lista di ID medianteremove
), oppure consente di rimuovere tutte le associazioni pre-esistenti (valorizzandoremoveAll: true
).
Come mostrato sopra, per le classi Team e Qualification vengono generati, rispettivamente, gli input TeamRoleRef
e QualificationRoleRefs
; questi compaiono nellâinput EmployeeUpdate
, rispettivamente nei campi dei ruoli team
(a uno) e qualification_
(a molti), nellâambito del servizio Mutation.Employee___update
.
In modo analogo, osservando il modello di riferimento, possiamo dedurre che nello schema GraphQL generato compaiono gli input ProjectRoleRef
ed EmployeeRoleRef
(anche questâultimo usato da EmployeeUpdate
).
input <ClassName>RoleObject(s)
#
Gli input type <ClassName>RoleObject
e <ClassName>RoleObjects
hanno lo scopo di gestire la creazione / modifica degli oggetti Part della classe <ClassName>
, nel contesto del servizio update
. Vengono generati per tutte le classi che hanno almeno un ruolo Part entrante managed; come suggerisce il nome, <ClassName>RoleObject
viene generato per composizioni a uno, mentre <ClassName>RoleRefs
viene generato per composizioni a molti.
input ClassNameRoleObject {
create: ClassNameCreate
update: ClassNameUpdate
delete: Boolean
}
input ClassNameRoleObjects {
create: [ClassNameCreate]
update: [ClassNameUpdate]
delete: [ID]
deleteAll: Boolean
}
Contestualmente alla modifica di un oggetto della classe Main, è possibile specificare, per campi relativi ai ruoli di composizione uscenti, quali oggetti delle corrispondenti classi Part si vogliono creare, modificare o distruggere:
<ClassName>RoleObject
consente di creare un nuovo oggetto part (popolando il campocreate
, di tipo<ClassName>Create
), aggiornare lâoggetto part pre-esistente (popolando il campoupdate
, di tipo<ClassName>Update
), oppure di eliminare lâoggetto part pre-esistente (valorizzandodelete: true
) ;<ClassName>RoleObjects
consente di specificare contemporaneamente la creazione contestuale di nuovi oggetti part (specificando una lista di<ClassName>Create
medianteadd
), la modifica contestuale di oggetti part pre-esistenti (specificando una lista di<ClassName>Update
medianteupdate
), lâeliminazione contestuale di oggetti part pre-esistenti (specificando una lista di ID mediantedelete
), oppure consente di eliminare tutti gli oggetti part pre-esistenti (valorizzandodeleteAll: true
).
Come mostrato sopra, per le classi Address e Project_assignment vengono generati, rispettivamente, gli input AddressRoleObject
e Project_assignmentRoleObjects
; questi compaiono nellâinput EmployeeUpdate
, rispettivamente nei campi dei ruoli address
(a uno) e assignments
(a molti), nellâambito del servizio Mutation.Employee___update
.
input <ClassName>DraftUpdate
#
Lâinput type <ClassName>DraftUpdate
è la variante di <ClassName>Update
usata dai servizi previewUpdate
e validateUpdate
. Non ha sostanziali differenze con la sua controparte, se non per le strutture usate come valore dei campi relativi a ruoli part uscenti.
Ad esempio, per la classe Employee, si ha la seguente struttura EmployeeDraftUpdate
:
input EmployeeDraftUpdate {
_id: ID! # come per EmployeeUpdate
# attributi (stessi di EmployeeUpdate)
first_name: String
last_name: String
date_of_birth: Date
email_address: String
date_joined: Date
hourly_cost: Real
username: String
# ruoli uscenti
#...
}
I ruoli uscenti vengono invece mappati come segue:
- associazioni a uno:
<TargetClassName>DraftUpdateRoleRef
(analogo a<ClassName>DraftUpdateRoleRef
); - associazioni a molti:
<TargetClassName>RoleRefs
(analogo a<ClassName>DraftUpdateRoleRefs
); - part a uno:
<PartClassName>DraftUpdateRoleObject
(analogo a<ClassName>DraftUpdateRoleObject
); - part a molti:
<PartClassName>DraftUpdateRoleObjects
(analogo a<ClassName>DraftUpdateRoleObjects
).
Per Employee, i ruoli uscenti vengono mappati come segue:
input EmployeeDraftUpdate {
# attributi (stessi di EmployeeUpdate)
# ...
# ruoli uscenti
team: TeamDraftUpdateRoleRef # associazione a 1
supervisor: EmployeeDraftUpdateRoleRef # associazione riflessiva a 1
address: AddressDraftUpdateRoleObject # composizione a 1
qualification_: QualificationDraftUpdateRoleRefs # associazione a N
assignments: Project_assignmentDraftUpdateRoleObjects # composizione a N
}
input <ClassName>DraftUpdateRoleRef(s)
#
Gli input type <ClassName>DraftUpdateRoleRef
e <ClassName>DraftUpdateRoleRefs
sono rispettivamente le varianti di <ClassName>RoleRef
e <ClassName>RoleRefs
annidate allâinput <ClassName>DraftUpdate
usato dai servizi previewUpdate
e validateUpdate
. Al netto del nome diverso, queste strutture coincidono con le loro controparti, come possiamo vedere dal seguente confronto:
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)
#
Gli input type <ClassName>DraftUpdateRoleObject
e <ClassName>DraftUpdateRoleObjects
sono rispettivamente le varianti di <ClassName>RoleObject
e <ClassName>RoleObjects
annidate allâinput <ClassName>DraftUpdate
usato dai servizi previewUpdate
e validateUpdate
.
Rispetto a quanto detto per le strutture dei ruoli di associazione, queste differiscono anche semanticamente dalle loro controparti. Dal momento che gli input per oggetti part consentono la creazione contestuale, gli input DraftUpdate
e Update
usano rispettivamente <ClassName>DraftCreate
(che non ha campi required) <ClassName>Create
(che può avere campi required).
Di seguito vediamo il confronto con <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
}
Questa differenza nelle strutture annidate per i part giustifica, di fatto, la necessitĂ di avere una struttura DraftUpdate
dedicata per i servizi previewUpdate
e validateUpdate
, per garantire la stessa libertĂ di cui godono i servizi previewCreate
e validateCreate
.
Servizio updateBulk
#
Il servizio Mutation.<ClassName>___updateBulk
, controparte a molti del servizio update
, consente di aggiornare uno o piĂš oggetti a partire da una certa classe, tramite un grafo di modifiche che viene applicato in modo bulk a ciascun oggetto selezionato.
type Mutation {
ClassName___updateBulk(
options: ClassNamePageOptions!
data: ClassNameUpdateBulk!
forceWarnings: ForceWarnings
): ClassNameBulkResult
}
Il servizio richiede in input la struttura <ClassName>PageOptions
, opportunamente valorizzata per identificare lâinsieme di oggetti che si vuole modificare. Ă richiesto inoltre un input <ClassName>UpdateBulk
, che descrive le modifiche bulk che verranno applicate a ciascun oggetto selezionato.
Ă possibile aggirare eventuali warning restituiti dal server causati da Class Warning non bloccanti includendo lâargomento forceWarnings
, di tipo ForceWarnings
.
Il servizio restituisce in output un oggetto <ClassName>BulkResult
, che contiene, nel campo items
, la lista degli oggetti modificati dalla mutation.
type ClassNameBulkResult {
items: [ClassName!]!
totalCount: Int!
}
Ă possibile specificare quali campi si vogliono recuperare per ciascun record della lista, in modo analogo alla struttura <ClassName>Page
. Il campo totalCount
riporta il conteggio dei record
Ad esempio, il servizio per modificare diversi oggetti Employee è il seguente:
type Mutation {
Employee___updateBulk(
options: EmployeePageOptions!
data: EmployeeUpdateBulk!
forceWarnings: ForceWarnings
): EmployeeBulkResult
}
Servizio validateUpdateBulk
#
Il servizio Query.<ClassName>___validateUpdateBulk
riflette il servizio updateBulk
, ma con lo scopo di verificare se la modifica bulk degli oggetti non violi vincoli a livello di Data Validation.
type Query {
ClassName___validateUpdateBulk(options: ClassNamePageOptions!): ValidationResult
}
Il servizio richiede in input la struttura <ClassName>DraftUpdateBulk
e restituisce in output la struttura ValidationResult
, che indica se la modifica bulk è valida o meno e riporta eventuali Issue
a livello di Data Validation.
Ad esempio, il servizio per validare la modifica bulk di diversi oggetti Employee è il seguente:
type Query {
Employee___validateUpdateBulk(options: ClassNamePageOptions!): ValidationResult
}
input <ClassName>UpdateBulk
#
Lâinput type <ClassName>UpdateBulk
permette di specificare i dati necessari alla modifica bulk di oggetti pre-esistente della classe <ClassName>
. Questo input ricalca lâinput <ClassName>Update
, con unâunica differenza: il campo _id
è assente.
Il grafo di modifiche descritto da questa struttura non fa infatti riferimento a un oggetto pre-esistente della classe, bensĂŹ rappresenta le modifiche che verranno applicate in modo bulk a tutti gli oggetti selezionati dal servizio updateBulk
.
Ad esempio, per la classe Employee, si ha la seguente struttura EmployeeUpdateBulk
:
input EmployeeUpdateBulk {
# _id è assente
# attributi (stessi di EmployeeUpdate)
first_name: String
last_name: String
date_of_birth: Date
email_address: String
date_joined: Date
hourly_cost: Real
username: String
# ruoli uscenti (stessi di EmployeeUpdate)
team: TeamRoleRef # associazione a 1
supervisor: EmployeeRoleRef # associazione riflessiva a 1
address: AddressRoleObject # composizione a 1
qualification_: QualificationRoleRefs # associazione a N
assignments: Project_assignmentRoleObjects # composizione a N
}
input <ClassName>DraftUpdateBulk
#
Lâinput type <ClassName>DraftUpdateBulk
è la variante di <ClassName>UpdateBulk
usata dal servizio validateUpdateBulk
. Non ha sostanziali differenze con la sua controparte, se non per le strutture usate come valore dei campi relativi a ruoli part uscenti. Valgono infatti le stesse considerazioni fatte per lâinput <ClassName>DraftUpdate
.
Ad esempio, per la classe Employee, si ha la seguente struttura EmployeeDraftUpdateBulk
:
input EmployeeDraftUpdateBulk {
# _id è assente (come per EmployeeUpdateBulk)
# attributi (stessi di EmployeeDraftUpdate)
first_name: String
last_name: String
date_of_birth: Date
email_address: String
date_joined: Date
hourly_cost: Real
username: String
# ruoli uscenti (stessi di EmployeeDraftUpdate)
team: TeamDraftUpdateRoleRef # associazione a 1
supervisor: EmployeeDraftUpdateRoleRef # associazione riflessiva a 1
address: AddressDraftUpdateRoleObject # composizione a 1
qualification_: QualificationDraftUpdateRoleRefs # associazione a N
assignments: Project_assignmentDraftUpdateRoleObjects # composizione a N
}
Effettuare una scrittura generica #
Come visto finora, i servizi e le strutture offerti dallo schema per coprire gli scenari di Create e Update sono sostanzialmente molto simili tra loro; questo approccio è in linea col principio di prevedibilità dello schema GraphQL.
Nel progettare applicazioni che utilizzano intensivamente le API GraphQL (come un client web) può tuttavia non risultare necessario dover distinguere tra una scrittura di tipo Create o una Update; in questi casi, la tipizzazione rigida delle strutture viste finora può rappresentare un limite, in quanto lo sviluppatore è costretto a gestire separatamente questi due scenari.
Per questo motivo, a partire dalla versione di Livebase 5.8, lo schema offre dei servizi di scrittura generici (e relativi servizi di supporto Preview e Validate) che mettono a fattor comune queste differenze, che usano input flessibili in grado di assumere il significato di una Create o Update a seconda dei campi valorizzati.
Servizio save
#
Il servizio Mutation.<ClassName>___create
consente di creare un grafo di oggetti di una certa classe, oppure di aggiornare uno o piĂš oggetti della stessa classe, tramite un grafo di modifiche. Raggruppa i servizi create
e update
.
type Mutation {
ClassName___save(data: ClassNameDraft!, forceWarnings: ForceWarnings): ClassName
}
Il servizio richiede in input la struttura <ClassName>Draft
, e restituisce in output la struttura <ClassName>
contenente lâoggetto modificato. Ă possibile aggirare eventuali warning restituiti dal server causati da Class Warning non bloccanti includendo lâargomento forceWarnings
, di tipo ForceWarnings
.
Ad esempio, il servizio per aggiornare un oggetto Employee è il seguente:
type Mutation {
Employee___save(data: EmployeeDraft!, forceWarnings: ForceWarnings): Employee
}
Servizio preview
#
Il servizio Query.<ClassName>___previewCreate
consente di ottenere unâanteprima del risultato della creazione o dellâaggiornamento di un grafo di oggetti, utile per ottenere informazioni calcolate dal server, come attributi derivati e oggetti associabili al grafo; Raggruppa i servizi previewCreate
e previewUpdate
.
type Query {
ClassName___preview(data: ClassNameDraft!): ClassName
}
Il servizio richiede in input la struttura <ClassName>Draft
, e restituisce in output la struttura <ClassName>
, i cui campi calcolati sono pre-popolati, se possibile, a partire dalle informazioni inviate al server; gli associabili sono sempre disponibili per tutti i ruoli.
Ad esempio, il servizio per ottenere una preview di un oggetto Employee creato o aggiornato è il seguente:
type Query {
Employee___preview(data: EmployeeDraft!): Employee
}
Servizio validate
#
Il servizio Query.<ClassName>___validate
riflette il servizio save
, ma con lo scopo di verificare se il grafo di oggetti creato o modificato sia corretto a livello di Data Validation. Raggruppa i servizi validateCreate
e validateUpdate
.
type Query {
ClassName___validate(data: ClassNameDraft!): ValidationResult
}
Il servizio richiede in input la struttura <ClassName>Draft
e restituisce in output la struttura ValidationResult
, che indica se il grafo è valido o meno e riporta eventuali Issue
a livello di Data Validation.
Ad esempio, il servizio per validare la creazione o lâaggiornamento di un oggetto Employee pre-esistente è il seguente:
type Query {
Employee___validate(data: EmployeeDraft!): ValidationResult
}
input <ClassName>Draft
#
Lâinput type <ClassName>Draft
è la struttura di input piÚ flessibile tra quelle disponibili, ed è usata indistintamente dai servizi save
, preview
e validate
.
Come affermato qui, questo input flessibile è in grado di assumere il significato di una Create o Update a seconda dei campi valorizzati.
Gli attributi vengono mappati come segue:
- sono presenti solamente campi relativi ad attributi nativi (editabili), mentre sono esclusi attributi derivati e platform, a eccezione del campo
_id: ID!
; - a differenza di
<ClassName>Create
, i campi relativi ad attributi required non sono obbligatori; - a differenza di
<ClassName>Update
, anche il campo_id
non è obbligatorio. La presenza o meno di un valore per_id
distingue se lâoperazione è logicamente una Create o un Update;
Ad esempio, per la classe Employee, si ha la seguente struttura EmployeeDraft
:
input EmployeeUpdate {
_id: ID # non è obbligatorio
# attributi
first_name: String
last_name: String
date_of_birth: Date
email_address: String
date_joined: Date
hourly_cost: Real
username: String
# ruoli uscenti
# ...
}
I ruoli uscenti vengono mappati come segue:
- associazioni a uno:
ID
. Nel contesto di una Update, è possibile rimuovere lâassociazione esistente valorizzando questo campo anull
; - associazioni a molti:
<TargetClassName>DraftRoleRefs
(analogo a<ClassName>DraftRoleRefs
; - part a uno:
<PartClassName>Draft
(analogo a<ClassName>Create
). Nel contesto di una Update, è possibile aggiornare un oggetto part esistente includendo lâ_id
nella struttura annidata; è possibile inoltre eliminare il part esistente valorizzando questo campo anull
; - part a molti:
<PartClassName>DraftRoleObjects
(analogo a<ClassName>DraftRoleObjects
).
Per Employee, i ruoli uscenti vengono mappati come segue:
input EmployeeCreate {
# attributi
# ...
# ruoli uscenti
team: ID # associazione a 1
supervisor: ID # associazione riflessiva a 1
address: AddressDraft # composizione a 1
qualification_: QualificationDraftRoleRefs # associazione a N
assignments: Project_assignmentDraftRoleObjects # composizione a N
}
input <ClassName>DraftRoleRefs
#
Lâinput type <ClassName>DraftRoleRefs
ha lo scopo di gestire la modifica dellâassociazione verso la classe <ClassName>
nel contesto del servizio save
. Come la controparte <ClassName>RoleRefs
, viene generato per tutte le classi che hanno almeno unâassociazione a molti entrante managed. Al netto del nome diverso, questa struttura coincide con la sua controparte come possiamo vedere dal seguente confronto:
input ClassNameDraftRoleRefs {
add: [ID]
remove: [ID]
removeAll: Boolean
}
input ClassNameRoleRefs {
add: [ID]
remove: [ID]
removeAll: Boolean
}
input <ClassName>DraftRoleObjects
#
Lâinput type <ClassName>RoleObjects
ha lo scopo di gestire la creazione / modifica degli oggetti Part della classe <ClassName>
, nel contesto del servizio save
. Come la controparte <ClassName>RoleObjects
, viene generato per tutte le classi che hanno almeno un ruolo Part a molti entrante managed.
A differenza della sua controparte, questa struttura offre un unico campo save
(contenente una lista di <ClassName>Draft
) che accorpa i campi create
e update
e permette di specificare sia la creazione di nuovi oggetti part che la modifica di oggetti part esistenti. La semantica di ciascuna operazione è determinata dalla presenza di un valore nel campo _id
annidato di ciascun oggetto part.
Di seguito vediamo il confronto con <ClassName>RoleObjects
:
input ClassNameDraftRoleObjects {
save: [ClassNameDraft]
delete: [ID]
deleteAll: Boolean
}
input ClassNameRoleObjects {
create: [ClassNameCreate]
update: [ClassNameUpdate]
delete: [ID]
deleteAll: Boolean
}
Eliminare oggetti #
I seguenti servizi e strutture consentono di eliminare oggetti o testarne lâeliminazione.
Servizio delete
#
Il servizio Mutation.<ClassName>___delete
consente di eliminare un oggetto di una certa classe.
type Mutation {
ClassName___delete(_id: ID!, forceWarnings: ForceWarnings): DeleteResult
}
Il servizio richiede in input lâID dellâoggetto di <ClassName>
che si vuole eliminare, e restituisce in output un oggetto <DeleteResult>
; questâultimo contiene semplicemente il campo deleted: Boolean
, che vale true
se lâoggetto stato cancellato o false
altrimenti (a esempio, se non è stato trovato).
type DeleteResult {
deleted: Boolean
}
Ă possibile aggirare eventuali warning restituiti dal server causati da Class Warning non bloccanti includendo lâargomento forceWarnings
, di tipo ForceWarnings
.
Ad esempio, il servizio per eliminare un oggetto Employee è il seguente:
type Mutation {
Employee___delete(_id: ID!, forceWarnings: ForceWarnings): DeleteResult
}
che può essere utilizzato come segue:
mutation {
Employee___delete(_id: "10101") {
deleted
}
}
{
"deleted": true
}
Servizio validateDelete
#
Il servizio Query.<ClassName>___validateDelete
riflette il servizio delete
, ma con lo scopo di verificare se lâeliminazione dellâoggetto non violi vincoli a livello di Data Validation.
type Query {
ClassName___validateDelete(_id: ID!): ValidationResult
}
Il servizio richiede in input lâID dellâoggetto di <ClassName>
che si vuole eliminare, e restituisce in output la struttura ValidationResult
, che indica se lâeliminazione è valida o meno e riporta eventuali Issue
a livello di Data Validation.
Ad esempio, il servizio per validare lâeliminazione di un oggetto Employee è il seguente:
type Query {
Employee___validateDelete(_id: ID!): ValidationResult
}
Servizio deleteBulk
#
Il servizio Mutation.<ClassName>___deleteBulk
, controparte a molti del servizio delete
, consente di eliminare uno o piĂš oggetti di una certa classe.
type Mutation {
ClassName___deleteBulk(
options: ClassNamePageOptions!
forceWarnings: ForceWarnings
): DeleteBulkResult
}
Il servizio richiede in input la struttura <ClassName>PageOptions
, opportunamente valorizzata per identificare lâinsieme di oggetti che si vogliono eliminare, e restituisce in output un oggetto <DeleteBulkResult>
; questâultimo contiene semplicemente il campo deleted: Int
, a indicare il numero di oggetti eliminati.
type DeleteBulkResult {
deleted: Boolean
}
Ă possibile aggirare eventuali warning restituiti dal server causati da Class Warning non bloccanti includendo lâargomento forceWarnings
, di tipo ForceWarnings
.
Ad esempio, il servizio per eliminare diversi oggetti Employee è il seguente:
type Mutation {
Employee___deleteBulk(
options: EmployeePageOptions!
forceWarnings: ForceWarnings
): DeleteBulkResult
}
Servizio validateDeleteBulk
#
Il servizio Query.<ClassName>___validateDeleteBulk
riflette il servizio deleteBulk
, ma con lo scopo di verificare se lâeliminazione degli oggetti non violi vincoli a livello di Data Validation.
type Query {
ClassName___validateDeleteBulk(options: ClassNamePageOptions!): ValidationResult
}
Il servizio richiede in input lâID dellâoggetto di <ClassName>
che si vuole eliminare, e restituisce in output la struttura ValidationResult
, che indica se lâeliminazione è valida o meno e riporta eventuali Issue
a livello di Data Validation.
Ad esempio, il servizio per validare lâeliminazione di diversi oggetti Employee è il seguente:
type Query {
Employee___validateDeleteBulk(options: ClassNamePageOptions!): ValidationResult
}
type ValidationResult
#
Tutti i servizi di tipo Validate menzionati finora (come Query.<ClassName>___validate
) ritornano la seguente struttura comune ValidationResult
:
type ValidationResult {
isValid: Boolean
issues: [Issue]
}
Il campo isValid
vale true
se, nel verificare la correttezza della chiamata a livello di Data Validation, il server ha sollevato Issue
di questo tipo. Le stesse Issue
sono riportate nel campo issues
.
Ă importante notare che i servizi di tipo Validate sono gli unici in grado di ottenere Issue
dal server GraphQL nel campo data
, e non annidate nel campo errors
; questo perchĂŠ, nel comunicare le Issue, il server non va in eccezione. Tuttavia, questi servizi sono in grado di ârilevareâ solamente Issue
a livello di Data Validation. Maggiori informazioni sono disponibili qui: Reference: tipi di Issue
.
input ForceWarnings
#
Come affermato in Lo schema GraphQL: Errori lato server, di default il server ritorna un errore GraphQL sia che nellâapplicazione si verifichino errori (bloccanti) che warning (non bloccanti), come accade quando un Class warning è impostato per non impedire allâutente di eseguire unâazione.
In GraphQL, alcuni servizi consentono di âforzareâ i warning in modo che questi non blocchino lâesecuzione.
I servizi di scrittura di tipo mutation elencati sono forzabili mediante lâargomento forceWarnings
:
Mutation.<ClassName>___create
;Mutation.<ClassName>___update
;Mutation.<ClassName>___updateBulk
;Mutation.<ClassName>___save
;Mutation.<ClassName>___delete
;Mutation.<ClassName>___deleteBulk
.
Lâinput type ForceWarnings
consiste di due opzioni:
input ForceWarnings {
actionVeto: Boolean = false
dataValidation: Boolean = false
}
Queste consentono di forzare i warning sollevati dal server in risposta agli eventi per cui è possibile impostare un Class Warning.
Sostanzialmente, actionVeto
si riferisce ai seguenti eventi:
- Edit: avviene quando da GraphQL viene invocato un servizio che modifica dati esistenti (
update
,save
,delete
, âŚ); - Create: avviene quando da GraphQL viene invocato un servizio che crea nuovi dati (
create
,save
).
Dâaltra parte, dataValidation
si riferisce ai Database events in figura, ovvero eventi relativi al persist dei dati nellâapplicazione generata.
Valorizzando uno di questi due campi a true
, è possibile aggirare eventuali Class Warning non bloccanti definiti definiti sulla nostra classe di interesse.
Vediamo un esempio. Supponiamo di avere definito il seguente Class Warning non bloccante su Employee:
La seguente chiamata fallisce:
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 with respect to 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
}
}
Il server ha restituito una sola Issue
di livello WARNING
. Invece di cambiare il valore del campo hourly_cost
, modifichiamo la chiamata affinchĂŠ forzi il Class warning e verifichiamo che la successiva chiamata vada a buon fine:
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
}
}
}