Tutorial: Programmatically creating a GraphQL endpoint for an admin team
In this tutorial, you will programmatically create a GraphQL endpoint so that an administrator can manage it later in Brightspot. The endpoint requirements are as follows:
- The endpoint should be able to fetch data from a content type that you define.
- When the endpoint is deployed, it should be immediately available to the administrator.
- The administrator should be able to control whether or not an API key is required.
- The administrator should be able to control whether or not introspective queries are allowed in certain environments.
Because your admin team includes individuals of varying technical expertise, you will limit the number of configurable fields to only the two listed above—Access Option and Allow Introspection Queries—helping prevent mis-configuration of the endpoint. For a look into the administrator's role in this process, see Tutorial: Creating and expanding access for an endpoint in Brightspot.
In this tutorial, you will:
- Create a data model and view model.
- Create a Java class that will contain the endpoint configuration.
- Add a URL path from which the endpoint can be accessed.
- Configure the endpoint to fetch data from the data model.
- Allow an administrator to control whether or not an API key is required.
- Allow an administrator to control whether or not to allow introspective queries.
Assumptions
This tutorial assumes the following:
- Familiarity with Brightspot's Java APIs
- Familiarity with GraphQL
As a prerequisite for this tutorial, you will create two Java classes for your data model and view model, respectively.
For the data model, you will create a Java class named Product
that extends Content
.
public class Product extends Content {
private String name;
private String description;
private String color;
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public String getColor() {
return color;
}
}
For the view model, you will create a Java class named ProductViewModel
that extends ViewModel<Product>
.
@ViewInterface
public class ProductViewModel extends ViewModel<Product> {
public String getColor() {
return model.getColor();
}
public String getName() {
return model.getName();
}
public String getMessage() {
return String.format("%s: %s: %s", getName(), getColor(), model.getDescription());
}
}
In this step, you will be creating a single instance of a Java class named ProductDeliveryEndpoint
that extends ContentDeliveryApiEndpoint
. Because you will implement Singleton
, the endpoint will be immediately available to the administrator when you deploy the code.
@DisplayName("Product Delivery API")
public class ProductDeliveryEndpoint extends ContentDeliveryApiEndpoint implements Singleton {
In this step, you will specify a URL path with which to hit the endpoint. Endpoint URL paths are tied to Brightspot's permalink system, which automatically validates a given URL path for uniqueness.
@Override
public Set<String> getPaths() {
return Collections.singleton("/product-delivery-api");
}
-
This API allows you to specify multiple URL paths from which the endpoint can be accessed; however, for the purposes of this tutorial, you will create only one.
https://cms.example.com/product-delivery-api-endpoint
).
In this step, you will configure the endpoint so that it can fetch data. You will do this by creating a query entry field—a top-level field on the Query
type—for use with your endpoint.
@Override
public List<ContentDeliveryEntryPointField> getQueryEntryFields() {
return Collections.singletonList(
new ContentDeliveryEntryPointField(ProductViewModel.class));
}
-
References the view model you created in step 2 of this tutorial.
In this step, you will make the endpoint's access level configurable for an administrator. Over the course of the exercise, you will expand the code to control the future use of the field and prevent a validation error from occurring upon save.
- Define the editorial field.
@Embedded private GraphQLApiAccessOption accessOption;
- Override the
getApiAccessOption
method, returning the editorially-specified value of the newly added field.@Override public GraphQLApiAccessOption getApiAccessOption() { return accessOption; }
Make the field required.
We add the
@Required
annotation here which has the effect of an error message displaying in the Brightspot UI if a user attempts to submit the form with a blank value for the field.@Embedded @Required private GraphQLApiAccessOption accessOption;
Prevent a validation exception on save.
Because it is a
Singleton
, Brightspot will attempt to save the instance of the endpoint the first time you deploy the code. Since the Access Option field has no value yet, the save would fail with a validation error (because of the aforementioned@Required
annotation). The followingbeforeSave()
implementation mitigates this problem by defaulting the value to an option that requires an API key.@Override protected void beforeSave() { super.beforeSave(); if (accessOption == null) { accessOption = new GraphQLApiAccessOptionExplicit(); } }
Your code should now look similar to the following:
@Embedded
@Required
private GraphQLApiAccessOption accessOption;
@Override
public GraphQLApiAccessOption getApiAccessOption() {
return accessOption;
}
@Override
protected void beforeSave() {
super.beforeSave();
if (accessOption == null) {
accessOption = new GraphQLApiAccessOptionExplicit();
}
}
For the purposes of this tutorial, the company that gave you these endpoint requirements has three environments: QA, UAT, and Production. An administrator may want to configure whether introspection queries are allowed on a per-environment basis; to facilitate that, in this step you will add a Allow Introspection Queries toggle field to Brightspot.
private Boolean allowIntrospectionQueries;
@Override
public IntrospectionQueryRule getIntrospectionQueryRule() {
return () -> Boolean.TRUE.equals(allowIntrospectionQueries);
}
All together, your data model, view model, and endpoint now resemble the following code snippets:
Product.java
public class Product extends Content {
private String name;
private String description;
private String color;
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public String getColor() {
return color;
}
}
ProductViewModel.java
@ViewInterface
public class ProductViewModel extends ViewModel<Product> {
public String getColor() {
return model.getColor();
}
public String getName() {
return model.getName();
}
public String getMessage() {
return String.format("%s: %s: %s", getName(), getColor(), model.getDescription());
}
}
ProductDeliveryEndpoint.java
@Recordable.DisplayName("Product Delivery API")
public class ProductDeliveryEndpoint extends ContentDeliveryApiEndpoint implements Singleton {
@Embedded
@Required
private GraphQLApiAccessOption accessOption;
private Boolean allowIntrospectionQueries;
@Override
public List<ContentDeliveryEntryPointField> getQueryEntryFields() {
return Collections.singletonList(
new ContentDeliveryEntryPointField(ProductViewModel.class));
}
@Override
public Set<String> getPaths() {
return Collections.singleton("/product-delivery-api");
}
@Override
public GraphQLApiAccessOption getApiAccessOption() {
return accessOption;
}
@Override
public IntrospectionQueryRule getIntrospectionQueryRule() {
return () -> Boolean.TRUE.equals(allowIntrospectionQueries);
}
@Override
protected void beforeSave() {
super.beforeSave();
if (accessOption == null) {
accessOption = new GraphQLApiAccessOptionExplicit();
}
}
}
The Brightspot UI now looks similar to the following image.