= GraphQL Viewer

:Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR  CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

The GraphQL viewer automatically exposes an Apache Causeway domain object model as a link:https://graphql.org/[GraphQL] API.
The viewer iterates over the domain services and domain objects, with their actions, properties and collections represented as fields within the graph.
Actions that take parameters are mapped to fields with arguments.

The GraphQL viewer is implemented using link:https://spring.io/projects/spring-graphql[Spring GraphQL] project, which in turn is built on top of link:https://www.graphql-java.com/[GraphQL Java].
This also means that it automatically provides the link:https://github.com/graphql/graphiql[GraphiQL] interactive query tool.

== Concepts

[#api-variants]
=== API Variants

The GraphQL viewer supports 3 different API variants, controlled by the xref:refguide:config:sections/causeway.viewer.graphql.adoc#causeway.viewer.graphql.api-variant[causeway.viewer.graphql.api-variant] configuration property:

* `QUERY_ONLY`
+
Exposes only a Query API, of properties, collections and safe (query-onl) actions.
Any actions that mutate the state of the system (in other words are idempotent or non-idempotent are excluded from the API, as is the ability to set properties.

* `QUERY_AND_MUTATIONS`
+
Exposes an API with Query for query/safe separate queries and field access, with mutating (idempotent and non-idempotent) actions and property setters instead surfaced as Mutations, as per the link:https://spec.graphql.org/June2018/#sec-Language.Operations[GraphQL spec].

* `QUERY_WITH_MUTATIONS_NON_SPEC_COMPLIANT`
+
This variant exposes an API with both Query and Mutations, but relaxes the constraints for the Query API by also including idempotent and non-idempotent actions and property setters.
The actions _are_ also available through as Mutations (same as with `QUERY_AND_MUTATIONS` API variant).
+
Examples of using this API variant are provided in the xref:queries-that-are-also-mutations[Queries that are also Mutations] section, below.
+
[IMPORTANT]
Be aware that the resultant API is not compliant with the rules of the
GraphQL spec; in particular, it violates link:https://spec.graphql.org/June2018/#sec-Language.Operations[2.3 Operations] which states: "a query [is] a read‐only fetch";


In summary, the API variant therefore relates to whether and how mutating (non-safe) actions are represented.



[#rich-vs-simple-schemas]
=== Rich vs Simple Schemas

The viewer can represent the domain model as either a "rich" GraphQL schema, or a "simple" GraphQL schema; or indeed both can be supported ath the same time.

The *rich schema* represents all the behaviour and structure of the Causeway domain model:

* hidden, disabled, validate
* choices, autoComplete, defaults
* obtain ("get") the value

In other words, it exposes not only the core data/behaviour but also of the supporting methods.

The *simple schema* represents just the core data/behaviour, but does not include the supporting method facets.

With the rich schema, the application logic and business domain logic remains the concern of the server, with the calling client app responsible only for presentation logic.
In fact, the rich schemas is so rich that one could in theory implement a fully generic UI client.

With the simple schema, although the main business domain logic remains the concern of the server, the application logic moves to the client.
For example, if there is a drop-down list box, the client needs to know how to obtain the list of choices.
But the benefit of the simple schema is, well, that it is simpler; in other words more "intuitive" and less "verbose".

The xref:refguide:config:sections/causeway.viewer.graphql.adoc#causeway.viewer.graphql.schema-style[causeway.viewer.graphql.schema-style] configuration property specifies which schema (or both) is supported:

* `SIMPLE_ONLY` - exposes only the "simple" schema
* `RICH_ONLY` - exposes only the "rich" schema
* `SIMPLE_AND_RICH` - exposes both the simple and rich schemas, the top-level query for each residing under either the `simple` or `rich` field respectively.
+
For the top-level mutation, the "simple" schema is used.

* `RICH_AND_SIMPLE` - also exposes both the simple and rich schemas, the top-level query for each residing under either the `simple` or `rich` field respectively.
+
For the top-level mutation, the "rich" schema is used.

If only the simple or the rich schema is enabled (`SIMPLE_ONLY` or `RICH_ONLY`), then the set of types defined by each of these schemes is available at the root query.
If both schemas are enabled (`SIMPLE_AND_RICH` or `RICH_AND_SIMPLE`), the these each reside under a top-level field, by default "simple" or "rich".
The name of these fields can be configured to something else if required using xref:refguide:config:sections/causeway.viewer.graphql.adoc#causeway.viewer.graphql.top-level-field-name-for-simple[causeway.viewer.graphql.top-level-field-name-for-simple] and xref:refguide:config:sections/causeway.viewer.graphql.adoc#causeway.viewer.graphql.top-level-field-name-for-rich[causeway.viewer.graphql.top-level-field-name-for-rich] configuration properties.



== Example

How the viewer works is probably most easily explained by an example.
The diagram below shows a simple domain (in fact, this is the domain used by the GraphQL viewer's own tests):

image::test-domain.drawio.png[width=600]

GraphQL distinguishes queries and mutations, so let's look at each.
We'll assume here that the `SIMPLE_AND_RICH` schema style is in use.

NOTE: GraphQL also defines the notion of subscriptions; the GraphQL viewer currently has no support for these.


== Queries

Queries most often start at a domain service.
In the example above, these would be `Departments`, `DeptHeadRepository`, or `Staff`.

To list all ``Department``s, we can submit this query using either the "rich" schema or "simple" schema:

[cols="1a,1a", options="header"]
|===

|Rich schema
|Simple schema

|
[source,graphql]
----
{
  rich {                          #<.>
    university_dept_Departments { #<.>
      findAllDepartments {        #<.>
        invoke {                  #<.>
          results {               #<.>
            name {
              get                 #<.>
            }
            staffMembers {
              get {               #<.>
                name {
                  get             #<.>
                }
                _meta {
                  id              #<.>
                  logicalTypeName #<9>
                }
              }
            }
          }
        }
      }
    }
  }
}
----

|
[source,graphql]
----
{
  simple {                        #<1>
    university_dept_Departments { #<2>
      findAllDepartments {        #<3><4><5>
        name                      #<6>
        staffMembers {            #<7>
          name                    #<8>
          _meta {
            id                    #<9>
            logicalTypeName       #<9>
          }
        }
      }
    }
  }
}
----
|===
<.> specify schema style
<.> domain service
<.> action name
<.> invokes the action
<.> returning a list of ``Department``s
<.> gets (accesses) the `name` property of each returned `Department`
<.> also gets (accesses) the `staffMembers` collection of each returned `Department`, returning a list of ``StaffMember``s
<.> gets the `name` prperty for each returned `StaffMember`
<.> returns the internal id and logicalTypeName of each `StaffMember`.
Together, these make up the xref:refguide:applib:index/services/bookmark/Bookmark.adoc[] of the domain object.


Queries can also include parameters.

[cols="1a,1a", options="header"]
|===

|Rich schema
|Simple schema

|
[source,graphql]
----
{
  rich {
    university_dept_Departments {
      findDepartmentByName {
        invoke(name: "Classics") {
          results {
            name {
              get
            }
          }
        }
      }
    }
  }
}
----
|
[source,graphql]
----
{
  simple {
    university_dept_Departments {
      findDepartmentByName(name: "Classics") {
        name
      }
    }
  }
}
----
|===

The above is an example of invoking an action on a (singleton) domain service, but this works equally well on domain entities/view models once retrieved.
More on this below.


=== Supporting Metadata

If you use the "rich" schema, then as well as accessing properties and collections and invoking (safe) actions, the GraphQL viewer also allows access to the usual supporting metadata.
For example:

[cols="1a,1a", options="header"]
|===

|Rich schema
|Simple schema

|
[source,graphql]
----
{
  rich {
    university_dept_Departments {
      findAllDepartments {
        disabled          #<.>
        invoke {
          results {
            name {
              hidden        #<.>
            }
          }
        }
      }
    }
  }
}
----
|
Not supported by simple schema.
|===

<.> whether this action is disabled
<.> whether the property of the resultant object is hidden


Similarly, there are fields for action parameters:

* `validate` - is the proposed action parameter valid?
* `disable` - is the action or action parameter disabled?
* `choices` - for an action parameter, are their choices?
* `autoComplete` - for an action parameter, is there an auto-complete?
* `default` - for an action parameter, is there a default value?

There are also similar fields for properties:

* `validate` - is the proposed value of the property valid?
* `disable` - is the property disabled?
* `choices` - for a property, are their choices?
* `autoComplete` - for a property , is there an auto-complete?


[#the-meta-fieldtype]
== The Meta field/type

The `_meta` field provides access to additional information about the domain object.
Its full list of fields are:

* `logicalTypeName` and `id`; these are equivalent to the xref:refguide:applib:index/services/bookmark/Bookmark.adoc[] of the domain object
* `version` (if an entity and available)
* `title` (as per the xref:userguide:ROOT:ui-layout-and-hints.adoc#object-title[title()] supporting method)
* `icon` (as per the xref:userguide:ROOT:ui-layout-and-hints.adoc#object-icon[icon], normally the associated `.png` file) and `grid` (as per the xref:userguide:ROOT:ui-layout-and-hints.adoc[layout], normally the associated `.layout.xml` file )
+
These can only be downloaded if configured, see xref:#resources-blobs-clobs-layouts-icons[resources] section below.

* `cssClass` (as per the xref:userguide:ROOT:ui-layout-and-hints.adoc#object-css-styling[cssClass()] supporting method)

There is also one additional field, `saveAs`; this is discussed in the xref:testsupport.adoc[Test Support] section.


[#queries-that-lookup-a-domain-object]
== Queries that lookup a Domain Object

Most queries will start with a domain service, but it is also possible to define a query that starts with a "lookup" of existing domain object:

[cols="1a,1a",options="header"]
|===

|Rich schema
|Simple schema

|
[source,graphql]
----
{
  rich {
    university_dept_Department(   #<.>
      object: {id: "1"}           #<.>
    ) {
      name {
        get
      }
    }
  }
}
----
|
[source,graphql]
----
{
  simple {
    university_dept_Department(   #<1>
      object: {id: "1"}) {        #<2>
      name
    }
  }
}
----

|===
<.> logical type name of the domain object
<.> identifier of the domain object instance


The next section explains how use mutations to change the state of the system.


== Mutations

Actions that mutate the state of the system (with idempotent or non-idempotent xref:refguide:applib:index/annotation/Action.adoc#semantics[@Action#semantics]) are exposed as mutations.
Editable properties are also exposed as mutations.

IF the action is on a domain service, then the target is implicit; but if the action is on a domain object -- and also for properties -- then the target domain object must be specified.

For example, to invoke a mutating action on a domain service:

[cols="1a,1a", options="header"]
|===

|Rich schema
|Simple schema

|
[source,graphql]
----
mutation {
  university_dept_Departments__createDepartment(  #<.>
      name: "Geophysics",
      deptHead: null
  ) {
    name {
      get
    }
  }
}
----

|
[source,graphql]
----
mutation {
  university_dept_Departments__createDepartment(
      name: "Geophysics",
      deptHead: null
  ) {
    name
  }
}
----

|===
<.> derived from the logical type name of the domain service, and the action Id.


For example, to invoke a mutating action on a domain object

[cols="1a,1a", options="header"]
|===

|Rich schema
|Simple schema

|
[source,graphql]
----
mutation {
  university_dept_Department__changeName(     # <.>
      _target: {id : "1"},                     # <.>
      newName: "Classics and Ancient History"
  ) {
    name {
      get
    }
  }
}
----

|
[source,graphql]
----
mutation {
  university_dept_Department__changeName(
      _target: {id : "1"},
      newName: "Classics and Ancient History"
  ) {
    name
  }
}
----

|===
<.> derived from the logical type name of the domain object, and the action Id.
<.> the `object` argument specifies the target object


Or, to set a property on a domain object:

[cols="1a,1a", options="header"]
|===

|Rich schema
|Simple schema

|
[source,graphql]
----
mutation {
  university_dept_StaffMember__name(  #<.>
      _target: {id: "1"},             #<.>
      name: "Jonathon Gartner"
  ) {
    name {                            #<.>
      get
    }
  }
}
----

|
[source,graphql]
----
mutation {
  university_dept_StaffMember__name(
      _target: {id: "1"},
      name: "Jonathon Gartner"
  ) {
    name
  }
}
----

|===
<.> derived from the logical type name of the domain object, and the property Id.
<.> the `_target` argument specifies the target object
<.> property setters are `void`, so as a convenience the mutator instead returns the target object.


[#queries-that-are-also-mutations]
== Queries that are also Mutations

According to the link:https://spec.graphql.org/June2018/#sec-Language.Operations[GraphQL specification], queries should be read-only; they must not change the state of the system.

Enabling the `QUERY_WITH_MUTATIONS_NON_SPEC_COMPLIANT` API variant (also mentioned xref:#api-variants[above]) relaxes this rule, allowing actions to be invoked that _do_ change the state of the system, and -- indeed -- allowing properties to be modified.
This is done through these additional fields:

* `invokeIdempotent` - to invoke an action whose action semantics are idempotent
+
As specified by xref:refguide:applib:index/annotation/Action.adoc#semantics[@Action#semantics].
* `invokeNonIdempotent` - to invoke an action whose action semantics are non-idempotent
* `set` - to modify a property.


For example, to invoke an action on a domain service:

[cols="1a,1a", options="header"]
|===

|Rich schema
|Simple schema

|
[source,graphql]
----
{
  rich {
    university_dept_Staff {
      createStaffMember {
        invokeNonIdempotent(
          name: "Dr. Georgina McGovern",
          department: { id: "1"}
      ) {
          results {
            name {
              get
            }
            department {
              get {
                name {
                  get
                }
              }
            }
          }
        }
      }
    }
  }
}
----

|
[source,graphql]
----
{
  simple {
    university_dept_Staff {
      createStaffMember (
          name: "Dr. Georgina McGovern",
          department: { id: "1"}
      ) {
        name
        department {
          name
        }
      }
    }
  }
}
----
|===

Or, to find a domain object and then invoke a mutating action on it:

[cols="1a,1a", options="header"]
|===

|Rich schema
|Simple schema

|
[source,graphql]
----
{
  rich {
    university_dept_DeptHeads {
      findHeadByName {
        invoke(name: "Prof. Dicky Horwich") {
          results {
            changeName {
              invokeIdempotent(newName: "Prof. Richard Horwich") {
                results {
                  name {
                    get
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
----
|
[source,graphql]
----
{
  simple {
    university_dept_DeptHeads {
      findHeadByName(name: "Prof. Dicky Horwich") {
        changeName(newName: "Prof. Richard Horwich") {
          name
        }
      }
    }
  }
}
----
|===

Or, similarly to find a domain object and then set a property afterwards:

[cols="1a,1a", options="header"]
|===

|Rich schema
|Simple schema

|
[source,graphql]
----
{
  rich {
    university_dept_Staff {
      findStaffMemberByName {
        invoke(name: "Gerry Jones") {
          results {
            name {
              set(name: "Gerald Johns") {
                name {
                  get
                }
              }
            }
          }
        }
      }
    }
  }
}
----

|
Not supported by simple schema

|===




[#resources-blobs-clobs-layouts-icons]
== Resources (Blobs, Clobs, Layouts, Icons)

Rather than returning the values of Blobs and Clobs inline within a response, instead the GraphQL viewer renders these as a URL to a resource controller.
The client can then make a second call to this endpoint using a simple HTTP(s) GET.

The same approach is used for both simple and rich schemas.

For example:

[cols="1a,1a", options="header"]
|===

|Rich schema
|Simple schema

|
[source,graphql]
----
{
  rich {
    university_dept_Staff {
      findStaffMemberByName {
        invoke(name: "Gerry Jones") {
          results {
            photo {
              get {
                bytes     # <.>
              }
            }
          }
        }
      }
    }
  }
}
----
<.> requests a URL to download bytes

|
[source,graphql]
----
{
  simple {
    university_dept_Staff {
      findStaffMemberByName(name: "Gerry Jones") {
        photo {
          bytes           # <1>
        }
      }
    }
  }
}
----
|===

This will result in a response (for the rich schema) such as:

[source,json]
----
{
  "data" : {
    "rich" : {
      "university_dept_Staff" : {
        "findStaffMemberByName" : {
          "invoke" : {
            "results" : {
              "photo" : {
                "get" : {
                  "bytes" : "///graphql/object/university.dept.StaffMember:123/photo/blobBytes"
                }
              }
            }
          }
        }
      }
    }
  }
}
----

The simple schema's response is very similar.


[NOTE]
====
The viewer does _not_ currently provide any way to update Blobs or Clobs.
One option is to implement a custom controller that the client can post to, analogous to the in-built resource controller.
====


The xref:#the-meta-fieldtype[meta field] mentioned earlier also allows the `icon` and `grid` (layout) files to be downloaded in a similar way:

[cols="1a,1a", options="header"]
|===

|Rich schema
|Simple schema

|
[source,graphql]
----
{
  rich {
    university_dept_Staff {
      findStaffMemberByName {
        invoke(name: "Gerry Jones") {
          results {
            _meta {
              icon        # <.>
              grid        # <.>
            }
          }
        }
      }
    }
  }
}
----
<.> requests a URL to download bytes

|
[source,graphql]
----
{
  simple {
    university_dept_Staff {
      findStaffMemberByName(name: "Gerry Jones") {
        _meta {
          icon            # <1>
          grid            # <2>
        }
      }
    }
  }
}
----

|===
<.> URL to download the icon (typically a `.png` file)
<.> URL to download the grid layout (typically the `.layout.xml` file)


Because the resource controller exposes information directly, these fields are suppressed by default.
To enable, use the xref:refguide:config:sections/causeway.viewer.graphql.adoc#causeway.viewer.graphql.resources.response-type[causeway.viewer.graphql.resources.response-type] configuration property.
If you do this, then you should also make sure that the resource controller is made secure in some appropriate fashion.




