Migration Guide#
This migration guide serves to help integration developers in migrating their GraphQL queries from older versions of OS2mo's GraphQL interface to newer ones.
It is recommended that a systematic approach is token to migrating, upgrading from the current version to the subsequent one over and over again until the code is up-to-date with the latest version.
Below follows the migration guide for each version.
Version 22#
GraphQL version 22 introduces breaking changes to the org_unit_update
mutator, specifically with regards to the way UNSET
(arguments not send)
and null
/ None
(arguments explicitly to clear the value) are handled.
Prior to the change most fields on the mutator were required, with some
fields allowing null
s, however sending null
s did not clear the fields,
rather null
s where simply ignored similar to unset fields.
In the new version most fields are optional, with unset fields retaining
the prior behavior of not clearing the fields, but rather doing nothing,
while sending null
s now specifically request clearing the value of the
field (if possible).
Thus this commit enables HTTP PATCH semantics, where a single field can be updated and cleared without affecting the remaining fields on the object. This is the first mutator to undergo this treatment, but the plan is for all update mutators to exhibit this behavior.
As such to migrate from GraphQL v21, simply send the query as normal:
mutation OrgUnitUpdate($input: OrganisationUnitUpdateInput!) {
org_unit_update(input: $input) {
uuid
}
}
input
payload has undergone a
transformation, where all null
values are stripped from the payload:
{
"uuid": "08eaf849-e9f9-53e0-b6b9-3cd45763ecbb",
"validity": {"from": "2020-01-01"},
"name": "new_org_name",
"parent": null
}
```json
{
"uuid": "08eaf849-e9f9-53e0-b6b9-3cd45763ecbb",
"validity": {"from": "2020-01-01"},
"name": "new_org_name"
}
Version 21#
Prior to this version, the first from_date
or to_date
filter parameters were
automatically, implicitly, and forcefully inherited in all the following levels of the
query. For example, a query such as:
query GetOrgUnitEngagements {
org_units(filter: {from_date: null, to_date: null}) {
objects {
objects {
engagements(filter: {from_date: "1760-01-01", to_date: "1840-01-01"}) {
uuid
}
}
}
}
}
query GetOrgUnitEngagements {
org_units(filter: {from_date: null, to_date: null}) {
objects {
objects {
engagements {
uuid
}
}
}
}
}
engagements
will no longer implicitly inherit the {from_date: null, to_date: null}
filter from org_units
.
Users who wish to maintain the old behaviour should explicitly pass the same date filters at each level of their query:
query GetOrgUnitEngagements {
org_units(filter: {from_date: null, to_date: null}) {
objects {
objects {
engagements(filter: {from_date: null, to_date: null}) {
uuid
}
}
}
}
}
Version 20#
Prior to this version, facets
ignored start_date
and end_date
filtering.
For this reason, a query like
query GetFacet {
facets {
objects {
current {
user_key
}
}
}
}
current
objects:
{
"facets": {
"objects": [
{
"current": None,
}
]
}
}
This is no longer the case. Users who wish to retrieve the full facet history should utilise a query such as:
query GetFacet {
facets(filter: {from_date: null, to_date: null}) {
objects {
objects {
user_key
}
}
}
}
Version 19#
GraphQL version 19 introduces breaking changes to a few terminate endpoints.
Specifically the class_terminate
, facet_terminate
and itsystem_terminate
mutators.
The breaking changes are similar for all the endpoints, namely that the
validity
argument has been inlined, such that these mutators are now aligned
with the rest of the terminate mutators.
As such to migrate from GraphQL v18, simply send the input as normal:
mutation FacetTerminate($input: FacetTerminateInput!){
facet_terminate(input: $input) {
uuid
}
}
input
payload has undergone the following transformation:
{
"uuid": "0872fb72-926d-4c5c-a063-ff800b8ee697",
"validty": {
"to": "2023-01-01"
}
}
{
"uuid": "0872fb72-926d-4c5c-a063-ff800b8ee697",
"to": "2023-01-01"
}
Version 18#
GraphQL version 18 introduces breaking changes to the auditlog
's models
filter.
Specifically it changes the type from an unconstrained string to a structured enum.
To migrate to GraphQL v18, simply send queries as you do now, but modify the
models
filter value according to the below table:
v17 | v18 |
---|---|
AuditLog |
AUDIT_LOG |
Bruger |
PERSON |
Facet |
FACET |
ItSystem |
IT_SYSTEM |
Klasse |
CLASS |
Organisation |
ORGANISATION |
OrganisationEnhed |
ORGANISATION_UNIT |
OrganisationFunktion |
ORGANISATION_FUNCTION |
Additionally you should translate the model
field in responses according to the
above table as well.
Version 17#
GraphQL version 17 introduces breaking changes to a few class related endpoints.
Specifically the class_create
and the class_update
mutators.
The breaking changes to the class_create
and class_update
mutators are that
both now require a validity
argument in their input types. Allowing for bitemporal
changes to the underlying data-entity.
As such to migrate from GraphQL v16, simply send the input as normal:
mutation ClassUpdate($input: ClassUpdateInput!){
class_update(input: $input) {
uuid
}
}
input
payload has undergone the following transformation:
{
"uuid": "0872fb72-926d-4c5c-a063-ff800b8ee697",
"name": "TestClass",
"user_key": "testclass",
}
{
"uuid": "0872fb72-926d-4c5c-a063-ff800b8ee697",
"name": "TestClass",
"user_key": "testclass",
"validity": {
"from": null,
"to": null
}
}
Version 16#
GraphQL version 16 introduces breaking changes to a few facet related endpoints.
Specifically the facet_create
and the facet_update
mutators.
The breaking changes to the facet_create
and facet_update
mutators are that
both now require a validity
argument in their input types. Allowing for bitemporal
changes to the underlying data-entity.
As such to migrate from GraphQL v15, simply send the input as normal:
mutation FacetUpdate($input: FacetUpdateInput!){
facet_update(input: $input) {
uuid
}
}
input
payload has undergone the following transformation:
{
"uuid": "0872fb72-926d-4c5c-a063-ff800b8ee697",
"user_key": "Test",
}
{
"uuid": "0872fb72-926d-4c5c-a063-ff800b8ee697",
"user_key": "Test",
"validity": {
"from": null,
"to": null
}
}
Version 15#
GraphQL version 15 introduces breaking changes to a few itsystem related
endpoint. Specifically the itsystem
query endpoint, and the itsystem_create
and the itsystem_update
mutators.
The breaking changes to the itsystem
query endpoint as it is simply a filter that
has been given a new type, as such it only affects users that explicitly typed the filter
in their query, to migrate from GraphQL v14 in such case, simply rename the filter
type:
query ITSystemsRead($filter: BaseFilter){
itsystems(filter: $filter) {
...
}
}
query ITSystemsRead($filter: ITSystemFilter){
itsystems(filter: $filter) {
...
}
}
The breaking changes to the itsystem_create
and itsystem_update
mutators are
a little more involved. First up the itsystem_update
mutator now takes the
ITSystemUpdateInput
input-type, whereas GraphQL v14 unintentionally took the
ITSystemCreateInput
input-type. The newly introduced ITSystemUpdateInput
input-type is still identical to its create counterpart, but keeping them separate
allows for future non-breaking changes.
The ITSystemCreateInput
input-type itself has changed however as date arguments
are now given inside a nested validity
container, rather than on the input type
itself. As such to migrate from GraphQL v15, simply fix the input type:
mutation ITSystemUpdate($input: ITSystemCreateInput!){
itsystem_update(input: $input) {
uuid
}
}
mutation ITSystemUpdate($input: ITSystemUpdateInput!){
itsystem_update(input: $input) {
uuid
}
}
input
payload has undergone the following transformation:
{
"uuid": "0872fb72-926d-4c5c-a063-ff800b8ee697",
"user_key": "Test",
"name": "Test",
"from": "1990-01-01T00:00:00+01:00",
"to": null
}
{
"uuid": "0872fb72-926d-4c5c-a063-ff800b8ee697",
"user_key": "Test",
"name": "Test",
"validity": {
"from": "1990-01-01T00:00:00+01:00",
"to": null
}
}
Version 14#
GraphQL version 14 introduces a breaking change to the filter variables
taken by the resolvers. Specifically, it moves them from the top-level
to a Filter
object.
To migrate from GraphQL v13, nest your filtering parameters in the
filter
object, e.g. from:
query AddressQuery {
addresses(
from_date: "2023-09-01",
address_type_user_keys: "EmailEmployee",
) {
objects {
uuid
}
}
}
query AddressQuery {
addresses(
filter: {
from_date: "2023-09-01",
address_type_user_keys: "EmailEmployee",
},
) {
objects {
uuid
}
}
}
Version 13#
GraphQL version 13 introduces a breaking change to the input variables taken
by the employee_create
and employee_update
-mutators. Specifically it
removes the name
, nickname
, cpr_no
, givenname
, from
and to
input
variable, which were previously optional arguments.
As such name
now has to be given using the structured form, using
given_name
and surname
, and correspondingly nickname
has to be
given using nickname_given_name
and nickname_surname
. cpr_no
should be given using cpr_number
while from
and to
should be given
via the validity
argument. All of these arguments have been made
required and the mutually exclusivity validators have been removed.
To migrate from GraphQL version 12, make the following changes to your code:
* rename givenname
to given_name
* rename cpr_no
to cpr_number
* restructure from
and to
to validity: {from: ..., to: ...}
* recode name
to given_name
/ surname
as:
given_name, surname = name.rsplit(" ", 1)
nickname
similarly to name
Version 12#
GraphQL version 12 introduces a breaking change to the input variables taken
by the ituser_create
-mutator. Specifically it removes the type
input
variable, which was previously an optional argument that should always have
its value set to: it
. Setting it to any other value will break
invariants in the underlying code leading to undefined behavior.
The argument had the default value of it
and as such the issue
only arises if the caller explicitly sends a different value.
The argument has however been removed entirely in version 12, as it
is leaking implementation-specific details and should never have been
exposed.
To migrate from GraphQL version 11, simply stop sending type
with your queries.
Version 11#
GraphQL version 11 introduces a breaking change to the input variables taken
by the manager_create
-mutator. Specifically it removes the type
input
variable, which was previously an optional argument that should always have
its value set to: manager
. Setting it to any other value will break
invariants in the underlying code leading to undefined behavior.
The argument had the default value of manager
and as such the issue
only arises if the caller explicitly sends a different value.
The argument has however been removed entirely in version 11, as it
is leaking implementation-specific details and should never have been
exposed.
To migrate from GraphQL version 10, simply stop sending type
with your queries.
Version 10#
GraphQL version 10 introduces a breaking change to the input variables taken
by the class_update
, facet_update
and itsystem_update
mutators.
These mutators took a separate uuid
input variable besides the one contained
within the input
object itself, leading to potential inconsistencies besides
the fact that it was not aligned with the other mutators.
In the future all mutators may get a uuid
selector (and have uuid
removed
from within the input
), but this change, should it occur, will be introduced
in a new version, and will make the change consistently across all mutators.
This change simply aims to ensure consistency across our existing mutators.
To migrate from version 9 to version 10, remove the uuid
parameter from
class_update
(or facet_update
/ itsystem_update
) and provide the uuid
inside the input
object instead.
Version 9:
mutation TestClassUpdate($input: ClassUpdateInput!, $uuid: UUID!) {
class_update(input: $input, uuid: $uuid) {
uuid
}
}
Version 10:
mutation TestClassUpdate($input: ClassUpdateInput!) {
class_update(input: $input) {
uuid
}
}
uuid
is now within input
.
Version 9#
GraphQL version 9 introduces a breaking change to the input variable name of the org_unit_terminate-mutator's input-variable, which did not align with the other mutators.
To migrate from version 8 to version 9, change the name of
org_unit_terminate
's input-variable from unit
to input
.
Version 8:
mutation TestTerminateOrgUnit($input: OrganisationUnitTerminateInput!) {
org_unit_terminate(unit: $input) {
uuid
}
}
Version 9:
mutation TestTerminateOrgUnit($input: OrganisationUnitTerminateInput!) {
org_unit_terminate(input: $input) {
uuid
}
}
Version 8#
GraphQL version 8 introduces a breaking change to the name of the
address_terminate
-mutator's input-variable, which didn't align with the other mutators.
To migrate from version 7 to version 8, change the name of
address_terminate
's input-variable from at
to input
.
Version 7:
mutation TestTerminateAddress($input: AddressTerminateInput!) {
address_terminate(at: $input) {
uuid
}
}
Version 8:
mutation TestTerminateAddress($input: AddressTerminateInput!) {
address_terminate(input: $input) {
uuid
}
}
Version 7#
GraphQL version 7 introduces a breaking change to the response formats for
the facet
, class
and itsystems
data-types, ensuring that these
top-level types now return formats akin to the rest of the data types
in the interface ensuring uniformity across (bi-)temporal data-types.
Please note that this change does not in fact implement (bi-)temporality for the mentioned data-types, but rather just adapt the interface in preparation of the future implementation of (bi-)temporality.
To migrate to the new schema, change all current GraphQL calls from
version 6 (/graphql/v6
) to version 7 (/graphql/v7
) and modify
queries for the mentioned data-types, from:
{
facets {
objects {
user_key
}
}
}
{
facets {
objects {
current {
user_key
}
}
}
}
current
wrapper object layer.
Version 6#
GraphQL version 6 introduces a breaking change to the facet_create
-mutator.
In version 5 the facet_create
-mutator is implemented atop the
RADataModel
's LoraFacet
and calls LoRa through the intern ASGI app.
However in version 6 the mutator is instead implemented as a very thin
translation directly atop LoRas create_or_import_object
function.
Although this change was planned to be a pure refactoring, several issues were identified during the refactoring, eventually leading to the refactoring becoming a breaking change motivating the new version of the GraphQL interface.
These issues are listed below, from the following motivating mutation:
mutation CreateFacet {
facet_create(input: {
type: "facet"
uuid: "00000000-fee1-baad-fa11-dead2badc0de"
user_key: "EmployeeAddressType"
org_uuid: "3b866d97-0b1f-48e0-8078-686d96f430b3"
parent_uuid: "182df2a8-2594-4a3f-9103-a9894d5e0c36"
}) {
uuid
}
}
type
is an optional argument that shall always have its value set to:facet
. Setting it to any other value will break invariants in the underlying code leading to undefined behavior. The argument has the default value offacet
and as such the issue only arises if the caller explicitly sends a different value. The argument has however been removed entirely in version 6, as it is leaking implementation-specific details and should never have been exposed.uuid
is an optional argument used for explicitly setting the UUID to be assigned to the newly created facet. We generally prefer entities to have randomly generated UUIDs instead of predetermined ones to avoid issues such as UUID conflicts. The argument has the default of using randomly generated UUIDs. The argument has been been removed entirely in version 6, opting to instead always generate random UUIDs for newly created facets.org_uuid
is a required argument that shall always have its value set to the root organisation's UUID. Setting it to any other value will break invariants in the underlying code leading to undefined behavior. The argument does not have a default value, and as such the caller has to look up the UUID of the root organisation whenever they want to create a new facet. The argument has been removed entirely in version 6, as it is leaking implementation-specific details and should never have been exposed.parent_uuid
is an optional argument, which was supposed to set the facet's parent relation to the UUID provided, however in version 5 it does nothing whatsoever, and is a completely ignored. The argument has been removed entirely in version 6, as having dead code exposed in the interface is an anti-pattern. The argument may be reintroduced in the future, but in a functional way.
Thus in version 6 the above motivation mutation would now look like the following instead:
mutation CreateFacet {
facet_create(input: {
user_key: "EmployeeAddressType"
}) {
uuid
}
}
To migrate from GraphQL version 6, simply stop sending type
, uuid
org_uuid
and facet_parent
with your queries. If you happen to "need"
to set the uuid
please get in contact so we can discuss potential
solutions.
Version 5#
GraphQL version 5 introduces a major breaking change to the entire reading schema, following from a restructuring to introduce pagination for all multi-element data-types.
The pagination is implemented via cursor-based pagination, for more details check: https://strawberry.rocks/docs/guides/pagination/cursor-based
The old non-paginated reading schema is still available in version 4 and all other previous versions.
In version 4 and previous versions querying high element count data-types had a tendency to timeout due to the sheer number of elements which had to be fetched and transformed from the database. The timeout issue could even occur on low element count data-types given sufficiently complex queries.
Various workarounds and hacks was being employed to work around this issue, such as implementing pagination for complex queries client side, by initially only requesting the UUIDs of elements in the first query, and then sending a sequence of complex queries each with a subset of the UUIDs returned by the initial query.
It is our hope that the new paginated version of the reading schema will eliminate the need for such workarounds, thus allowing the caller to specify their query with exactly the data that they need and desire, choosing a pagination limit that allows the query to return within the timeout limit.
To migrate to the new paginated schema, without actually utilizing the
pagination simply change all current GraphQL calls from version 4
(/graphql/v4
) to version 5 (/graphql/v5
), and modify your call from:
{
employees {
uuid
objects {
givenname
}
}
}
{
employees {
objects {
uuid
objects {
givenname
}
}
}
}
result = client.execute(query)
employees = result["employees"]
result = client.execute(query)
employees = result["employees"]["objects"]
To actually utilize the pagination a little more work must be put in. First the above query must be parameterized, as such:
query PaginatedEmployees($cursor: Cursor) {
employees(limit: 2, cursor: $cursor) {
objects {
uuid
objects {
user_key
}
}
page_info {
next_cursor
}
}
}
limit
parameter,
yielding a response alike this:
{
"data": {
"employees": {
"objects": [
... # 2 employee objects here
],
"page_info": {
"next_cursor": "Mg=="
}
}
}
}
next_cursor
("Mg=="
) in the
cursor
argument of our query in the next iteration, repeating for each
iteration until the next_cursor
becomes null
at which point the data
has been exhausted and all data entries have been iterated through.
It is very much recommended to put in the extra work and ensure that pagination is actually used, rather than just relying on everything being delivered as a single big page. Relying on this behavior is very likely to break the integration in the future as data quantities grow.
Version 4#
GraphQL version 4 introduces a breaking change to the class_create
-mutator.
In version 3 the class_create
-mutator is implemented atop the
ClassRequestHandler
and thus atop the entire Service API to LoRa
translation layer.
However in version 4 the mutator is instead implemented as a very thin
translation directly atop LoRas create_or_import_object
function.
Although this change was planned to be a pure refactoring, several issues were identified during the refactoring, eventually leading to the refactoring becoming a breaking change motivating the new version of the GraphQL interface.
These issues are listed below, from the following motivating mutation:
mutation CreateClass {
class_create(input: {
type: "class"
uuid: "00000000-fee1-baad-fa11-dead2badc0de"
name: "Office Number"
user_key: "EmployeeOfficeNumber"
scope:"TEXT"
facet_uuid: "5b3a55b1-958c-416e-9054-606b2c9e4fcd"
org_uuid: "3b866d97-0b1f-48e0-8078-686d96f430b3"
}) {
uuid
}
}
type
is an optional argument that shall always have its value set to:class
. Setting it to any other value will break invariants in the underlying code leading to undefined behavior. The argument has the default value ofclass
and as such the issue only arises if the caller explicitly sends a different value. The argument has however been removed entirely in version 4, as it is leaking implementation-specific details and should never have been exposed.uuid
is an optional argument used for explicitly setting the UUID to be assigned to the newly created class. We generally prefer entities to have randomly generated UUIDs instead of predetermined ones to avoid issues such as UUID conflicts. The argument has the default of using randomly generated UUIDs. The argument has been been removed entirely in version 4, opting to instead always generate random UUIDs for newly created classes.org_uuid
is a required argument that shall always have its value set to the root organisation's UUID. Setting it to any other value will break invariants in the underlying code leading to undefined behavior. The argument does not have a default value, and as such the caller has to look up the UUID of the root organisation whenever they want to create a new class. The argument has been removed entirely in version 4, as it is leaking implementation-specific details and should never have been exposed.
Thus in version 4 the above motivation mutation would now look like the following instead:
mutation CreateClass {
class_create(input: {
name: "Office Number"
user_key: "EmployeeOfficeNumber"
scope:"TEXT"
facet_uuid: "5b3a55b1-958c-416e-9054-606b2c9e4fcd"
}) {
uuid
}
}
To migrate from GraphQL version 3, simply stop sending type
, uuid
and org_uuid
with your queries. If you happen to "need" to set the
uuid
please get in contact so we can discuss potential solutions.
Version 3#
GraphQL version 3 introduced a breaking change to the healths top-level type by introducing pagination to the endpoint.
To query all healthchecks from OS2mo in GraphQL Version 2, run:
query {
healths {
identifier
status
}
}
{
"data": {
"healths": [
{
"identifier": "amqp",
"status": true
}
]
}
}
While to fetch the same data under GraphQL Version 3, one must run:
query {
healths {
objects {
identifier
status
}
}
}
{
"data": {
"healths": {
"objects": [
{
"identifier": "amqp",
"status": true
}
]
}
}
}
As can be seen Version 3 introduces an extra layer to the query and correspondingly to the response. Migrating thus simply requires expanding the queries with one extra layer, and similarly stripping one extra layer when processing the result.
Doing this bypasses the pagination by fetching everything as a single big page, and while this is generally discouraged, healths is one of the few data types for which it may make sense, as we do not expect a huge list of healthpoints to be introduced. That being said, this is no guarantee and not implementing the pagination means risking potential breakage in the future.
Version 2#
GraphQL version 2 introduced a breaking change to the parent relation on organisation units.
Assuming a query alike:
query OrganisationUnitParentQuery {
org_units(uuids: [$uuid]) {
current {
parent {
uuid
}
}
}
}
{
"data": {
"org_units": [
{
"current": {
"parent": [
{
"uuid": "2665d8e0-435b-5bb6-a550-f275692984ef"
}
]
}
}
]
}
}
{
"data": {
"org_units": [
{
"current": {
"parent": {
"uuid": "2665d8e0-435b-5bb6-a550-f275692984ef"
}
}
}
]
}
}
Thus to migrate from version 1 to version 2, simply remove whatever code that extracts the element from within the list, and use the element directly.