Add graphQL sample application
This commit adds a sample application to the repository. This application should not be published as part of the build. In this application, we're trying to showcase the various features and use cases with our Spring graphQL integration. We're mixing here data fetchers backed by datastores or remote hypermedia APIs. Closes gh-15
This commit is contained in:
23
graphql-sample/build.gradle
Normal file
23
graphql-sample/build.gradle
Normal file
@@ -0,0 +1,23 @@
|
||||
plugins {
|
||||
id 'org.springframework.boot' version '2.4.0-SNAPSHOT'
|
||||
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
|
||||
id 'java'
|
||||
}
|
||||
group = 'com.example'
|
||||
version = '0.0.1-SNAPSHOT'
|
||||
description = "GraphQL sample application"
|
||||
sourceCompatibility = '1.8'
|
||||
|
||||
dependencies {
|
||||
implementation project(':spring-graphql-web')
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-hateoas'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
||||
runtimeOnly 'com.h2database:h2'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
}
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package io.spring.sample.graphql;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class GraphQLSampleApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(GraphQLSampleApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package io.spring.sample.graphql.project;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Project {
|
||||
|
||||
private String slug;
|
||||
|
||||
private String name;
|
||||
|
||||
private String repositoryUrl;
|
||||
|
||||
private ProjectStatus status;
|
||||
|
||||
private List<Release> releases;
|
||||
|
||||
public String getSlug() {
|
||||
return this.slug;
|
||||
}
|
||||
|
||||
public void setSlug(String slug) {
|
||||
this.slug = slug;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getRepositoryUrl() {
|
||||
return this.repositoryUrl;
|
||||
}
|
||||
|
||||
public void setRepositoryUrl(String repositoryUrl) {
|
||||
this.repositoryUrl = repositoryUrl;
|
||||
}
|
||||
|
||||
public ProjectStatus getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
public void setStatus(ProjectStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public List<Release> getReleases() {
|
||||
return this.releases;
|
||||
}
|
||||
|
||||
public void setReleases(List<Release> releases) {
|
||||
this.releases = releases;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.spring.sample.graphql.project;
|
||||
|
||||
import graphql.schema.idl.RuntimeWiring;
|
||||
|
||||
import org.springframework.boot.graphql.RuntimeWiringCustomizer;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ProjectDataWiring implements RuntimeWiringCustomizer {
|
||||
|
||||
private final SpringProjectsClient client;
|
||||
|
||||
public ProjectDataWiring(SpringProjectsClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(RuntimeWiring.Builder builder) {
|
||||
builder
|
||||
.type("QueryType", typeWiring ->
|
||||
typeWiring.dataFetcher("project", env -> {
|
||||
String slug = env.getArgument("slug");
|
||||
return client.fetchProject(slug);
|
||||
}))
|
||||
.type("Project", typeWiring ->
|
||||
typeWiring.dataFetcher("releases", env -> {
|
||||
Project project = env.getSource();
|
||||
return client.fetchProjectReleases(project.getSlug());
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.spring.sample.graphql.project;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
|
||||
public enum ProjectStatus {
|
||||
ACTIVE, COMMUNITY, INCUBATING, ATTIC;
|
||||
|
||||
@JsonCreator
|
||||
public static ProjectStatus fromName(String name) {
|
||||
return Arrays.stream(ProjectStatus.values())
|
||||
.filter(type -> type.name().equals(name))
|
||||
.findFirst().orElse(ProjectStatus.ACTIVE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package io.spring.sample.graphql.project;
|
||||
|
||||
public class Release {
|
||||
|
||||
private String version;
|
||||
|
||||
private ReleaseStatus status;
|
||||
|
||||
private String referenceDocUrl;
|
||||
|
||||
private String apiDocUrl;
|
||||
|
||||
private boolean current;
|
||||
|
||||
public String getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public ReleaseStatus getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
public void setStatus(ReleaseStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getReferenceDocUrl() {
|
||||
return this.referenceDocUrl;
|
||||
}
|
||||
|
||||
public void setReferenceDocUrl(String referenceDocUrl) {
|
||||
this.referenceDocUrl = referenceDocUrl;
|
||||
}
|
||||
|
||||
public String getApiDocUrl() {
|
||||
return this.apiDocUrl;
|
||||
}
|
||||
|
||||
public void setApiDocUrl(String apiDocUrl) {
|
||||
this.apiDocUrl = apiDocUrl;
|
||||
}
|
||||
|
||||
public boolean isCurrent() {
|
||||
return this.current;
|
||||
}
|
||||
|
||||
public void setCurrent(boolean current) {
|
||||
this.current = current;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.spring.sample.graphql.project;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
|
||||
public enum ReleaseStatus {
|
||||
GENERAL_AVAILABILITY, MILESTONE, SNAPSHOT;
|
||||
|
||||
@JsonCreator
|
||||
public static ReleaseStatus fromName(String name) {
|
||||
return Arrays.stream(ReleaseStatus.values())
|
||||
.filter(type -> type.name().equals(name))
|
||||
.findFirst().orElse(ReleaseStatus.GENERAL_AVAILABILITY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package io.spring.sample.graphql.project;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.hateoas.CollectionModel;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.hateoas.client.Hop;
|
||||
import org.springframework.hateoas.client.Traverson;
|
||||
import org.springframework.hateoas.server.core.TypeReferences;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Component
|
||||
public class SpringProjectsClient {
|
||||
|
||||
private static final TypeReferences.CollectionModelType<Release> releaseCollection =
|
||||
new TypeReferences.CollectionModelType<Release>() { };
|
||||
|
||||
private final Traverson traverson;
|
||||
|
||||
public SpringProjectsClient(RestTemplateBuilder builder) {
|
||||
RestTemplate restTemplate = builder.messageConverters(Traverson.getDefaultMessageConverters(MediaTypes.HAL_JSON)).build();
|
||||
this.traverson = new Traverson(URI.create("https://spring.io/api/"), MediaTypes.HAL_JSON);
|
||||
this.traverson.setRestOperations(restTemplate);
|
||||
}
|
||||
|
||||
public Project fetchProject(String projectSlug) {
|
||||
return this.traverson.follow("projects")
|
||||
.follow(Hop.rel("project").withParameter("id", projectSlug))
|
||||
.toObject(Project.class);
|
||||
}
|
||||
|
||||
public List<Release> fetchProjectReleases(String projectSlug) {
|
||||
CollectionModel<Release> releases = this.traverson.follow("projects")
|
||||
.follow(Hop.rel("project").withParameter("id", projectSlug))
|
||||
.follow(Hop.rel("releases"))
|
||||
.toObject(releaseCollection);
|
||||
return new ArrayList(releases.getContent());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package io.spring.sample.graphql.repository;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
public interface ArtifactRepositories extends CrudRepository<ArtifactRepository, String> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.spring.sample.graphql.repository;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ArtifactRepositoriesInitializer implements ApplicationRunner {
|
||||
|
||||
private final ArtifactRepositories repositories;
|
||||
|
||||
public ArtifactRepositoriesInitializer(ArtifactRepositories repositories) {
|
||||
this.repositories = repositories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
List<ArtifactRepository> repositoryList = Arrays.asList(
|
||||
new ArtifactRepository("spring-releases", "Spring Releases", "https://repo.spring.io/libs-releases"),
|
||||
new ArtifactRepository("spring-milestones", "Spring Milestones", "https://repo.spring.io/libs-milestones"),
|
||||
new ArtifactRepository("spring-snapshots", "Spring Snapshots", "https://repo.spring.io/libs-snapshots")
|
||||
);
|
||||
repositories.saveAll(repositoryList);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package io.spring.sample.graphql.repository;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
@Entity
|
||||
public class ArtifactRepository {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String url;
|
||||
|
||||
private boolean snapshotsEnabled;
|
||||
|
||||
public ArtifactRepository(String id, String name, String url) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public ArtifactRepository() {
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public boolean isSnapshotsEnabled() {
|
||||
return snapshotsEnabled;
|
||||
}
|
||||
|
||||
public void setSnapshotsEnabled(boolean snapshotsEnabled) {
|
||||
this.snapshotsEnabled = snapshotsEnabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package io.spring.sample.graphql.repository;
|
||||
|
||||
import graphql.schema.idl.RuntimeWiring;
|
||||
|
||||
import org.springframework.boot.graphql.RuntimeWiringCustomizer;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ArtifactRepositoryDataWiring implements RuntimeWiringCustomizer {
|
||||
|
||||
private final ArtifactRepositories repositories;
|
||||
|
||||
public ArtifactRepositoryDataWiring(ArtifactRepositories repositories) {
|
||||
this.repositories = repositories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(RuntimeWiring.Builder builder) {
|
||||
builder.type("QueryType", typeWiring -> typeWiring
|
||||
.dataFetcher("artifactRepositories", env -> this.repositories.findAll())
|
||||
.dataFetcher("artifactRepository", env -> this.repositories.findById(env.getArgument("id"))));
|
||||
}
|
||||
}
|
||||
1
graphql-sample/src/main/resources/application.properties
Normal file
1
graphql-sample/src/main/resources/application.properties
Normal file
@@ -0,0 +1 @@
|
||||
management.endpoints.web.exposure.include=health,metrics,info
|
||||
43
graphql-sample/src/main/resources/schema.graphqls
Normal file
43
graphql-sample/src/main/resources/schema.graphqls
Normal file
@@ -0,0 +1,43 @@
|
||||
schema {
|
||||
query: QueryType
|
||||
}
|
||||
|
||||
type QueryType {
|
||||
artifactRepositories : [ArtifactRepository]
|
||||
artifactRepository(id : ID!) : ArtifactRepository
|
||||
project(slug: ID!): Project
|
||||
}
|
||||
|
||||
type ArtifactRepository {
|
||||
id: ID!
|
||||
name: String!
|
||||
url: String!
|
||||
snapshotsEnabled: Boolean
|
||||
}
|
||||
|
||||
type Project {
|
||||
slug: ID!
|
||||
name: String!
|
||||
repositoryUrl: String!
|
||||
status: ProjectStatus!
|
||||
releases: [Release]
|
||||
}
|
||||
|
||||
type Release {
|
||||
version: String!
|
||||
status: ReleaseStatus!
|
||||
current: Boolean
|
||||
}
|
||||
|
||||
enum ProjectStatus {
|
||||
ACTIVE
|
||||
COMMUNITY
|
||||
INCUBATING
|
||||
ATTIC
|
||||
}
|
||||
|
||||
enum ReleaseStatus {
|
||||
GENERAL_AVAILABILITY
|
||||
MILESTONE
|
||||
SNAPSHOT
|
||||
}
|
||||
@@ -15,3 +15,4 @@ pluginManagement {
|
||||
|
||||
rootProject.name = 'spring-graphql'
|
||||
include 'spring-graphql-web'
|
||||
include 'graphql-sample'
|
||||
|
||||
Reference in New Issue
Block a user