<commit_before>[[extending-graphql]]
==== Extending GraphQL

Extensions can contribute to the Ontrack <<integration-graphql,GraphQL>> core
schema:

* custom types
* root queries
* additional fields in <<model,project entities>>

[[extending-graphql-module]]
===== Preparing the extension

In your extension module, import the `ontrack-ui-graphql` module:

[source,groovy]
----
dependencies {
   compile "net.nemerosa.ontrack:ontrack-ui-graphql:${ontrackVersion}"
}
----

If you want to write <<extending-graphql-testing,integration tests>> for your
GraphQL extension, you have to include the GraphQL testing utilities:

[source,groovy]
----
dependencies {
   compile "net.nemerosa.ontrack:ontrack-ui-graphql:${ontrackVersion}:tests"
}
----

[[extending-graphql-types]]
===== Custom types

To define an extra type, you create a component which implements the
`GQLType` interface:

[source,java]
----
@Component
public class PersonType implements GQLType {
    @Override
    public GraphQLObjectType getType() {
        return GraphQLObjectType.newObject()
                .name("Person")
                .field(f -> f.name("name")
                        .description("Name of the person")
                        .type(GraphQLString)
                )
                .build();
    }
}
----

TIP: See the https://github.com/graphql-java/graphql-java[graphql-java]
documentation for the description of the type construction.

You can use this component in other ones, like in queries, field definitions
or other types, like shown below:

[source,java]
----
@Component
public class AccountType implements GQLType {

    private final PersonType personType;

    @Autowired
    public AccountType (PersonType personType) {
        this.personType = personType;
    }

    @Override
    public GraphQLObjectType getType() {
        return GraphQLObjectType.newObject()
                .name("Account")
                .field(f -> f.name("username")
                        .description("Account name")
                        .type(GraphQLString)
                )
                .field(f -> f.name("identity")
                        .description("Identity")
                        .type(personType.getType())
                )
                .build();
    }
}
----

You can also create GraphQL types dynamically by using introspection of your
model classes.

Given the following model:

[source,java]
----
@Data
public class Person {
   private final String name;
}
@Data
public class Account {
   private final String username;
   private final Person identity;
}
----

You can generate the `Account` type by using:

[source,java]
----
@Override
public GraphQLObjectType getType() {
   return GraphQLBeanConverter.asObjectType(Account.class);
}
----

CAUTION: The `GraphQLBeanConverter.asObjectType` is still very
*experimental* and its implementation is likely to change in the next versions
of Ontrack.

[[extending-graphql-queries]]
===== Root queries

[[extending-graphql-fields]]
===== Extra fields

[[extending-graphql-scalars]]
===== Built-in scalar fields

The Ontrack GraphQL module adds the following scalar types, which you can
use in your field or type definitions:

* `GQLScalarJSON.INSTANCE` - maps to a `JsonNode`
* `GQLScalarLocalDateTime.INSTANCE` - maps to a `LocalDateTime`
<commit_msg>#458 Doc - root queries<commit_after>[[extending-graphql]]
==== Extending GraphQL

Extensions can contribute to the Ontrack <<integration-graphql,GraphQL>> core
schema:

* custom types
* root queries
* additional fields in <<model,project entities>>

[[extending-graphql-module]]
===== Preparing the extension

In your extension module, import the `ontrack-ui-graphql` module:

[source,groovy]
----
dependencies {
   compile "net.nemerosa.ontrack:ontrack-ui-graphql:${ontrackVersion}"
}
----

If you want to write <<extending-graphql-testing,integration tests>> for your
GraphQL extension, you have to include the GraphQL testing utilities:

[source,groovy]
----
dependencies {
   compile "net.nemerosa.ontrack:ontrack-ui-graphql:${ontrackVersion}:tests"
}
----

[[extending-graphql-types]]
===== Custom types

To define an extra type, you create a component which implements the
`GQLType` interface:

[source,java]
----
@Component
public class PersonType implements GQLType {
    @Override
    public GraphQLObjectType getType() {
        return GraphQLObjectType.newObject()
                .name("Person")
                .field(f -> f.name("name")
                        .description("Name of the person")
                        .type(GraphQLString)
                )
                .build();
    }
}
----

TIP: See the https://github.com/graphql-java/graphql-java[graphql-java]
documentation for the description of the type construction.

You can use this component in other ones, like in queries, field definitions
or other types, like shown below:

[source,java]
----
@Component
public class AccountType implements GQLType {

    private final PersonType personType;

    @Autowired
    public AccountType (PersonType personType) {
        this.personType = personType;
    }

    @Override
    public GraphQLObjectType getType() {
        return GraphQLObjectType.newObject()
                .name("Account")
                .field(f -> f.name("username")
                        .description("Account name")
                        .type(GraphQLString)
                )
                .field(f -> f.name("identity")
                        .description("Identity")
                        .type(personType.getType())
                )
                .build();
    }
}
----

You can also create GraphQL types dynamically by using introspection of your
model classes.

Given the following model:

[source,java]
----
@Data
public class Person {
   private final String name;
}
@Data
public class Account {
   private final String username;
   private final Person identity;
}
----

You can generate the `Account` type by using:

[source,java]
----
@Override
public GraphQLObjectType getType() {
   return GraphQLBeanConverter.asObjectType(Account.class);
}
----

CAUTION: The `GraphQLBeanConverter.asObjectType` is still very
*experimental* and its implementation is likely to change in the next versions
of Ontrack.

[[extending-graphql-queries]]
===== Root queries

Your extension can contribute to the root query by creating a component
implementing the `GQLRootQuery` interface:

[source,java]
----
@Component
public class AccountGraphQLRootQuery implements GQLRootQuery {

    private final AccountType accountType;

    @Autowired
    public AccountGraphQLRootQuery(AccountType accountType) {
        this.accountType = accountType;
    }

    @Override
    public GraphQLFieldDefinition getFieldDefinition() {
        return GraphQLFieldDefinition.newFieldDefinition()
                .name("accounts")
                .argument(a -> a.name("username")
                        .description("User name pattern")
                        .type(GraphQLString)
                )
                .type(accountType.getType())
                .dataFetcher(...)
                .build();
    }
}
----

This root query can then be used into your GraphQL queries:

[source]
----
{
   accounts(username: "admin*") {
      username
      identity {
         name
      }
   }
}
----

[[extending-graphql-fields]]
===== Extra fields

[[extending-graphql-scalars]]
===== Built-in scalar fields

The Ontrack GraphQL module adds the following scalar types, which you can
use in your field or type definitions:

* `GQLScalarJSON.INSTANCE` - maps to a `JsonNode`
* `GQLScalarLocalDateTime.INSTANCE` - maps to a `LocalDateTime`

{'fraction_non_alphanumeric': 0.10846214304427561, 'fraction_numerical': 0.0003929787791459261, 'mean_word_length': 2.699127906976744, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 22, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7244902', 'n_tokens_mistral': 2117, 'n_tokens_neox': 2045, 'n_words': 625}