Skip to content

Commit

Permalink
Migration of GraphQL todo example app to guide
Browse files Browse the repository at this point in the history
  • Loading branch information
sdelamo committed Mar 4, 2022
1 parent ae8d20d commit 4e4e9f4
Show file tree
Hide file tree
Showing 18 changed files with 475 additions and 1 deletion.
1 change: 1 addition & 0 deletions buildSrc/src/main/java/io/micronaut/guides/Category.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public enum Category {
EMAIL("Email"),
DATA_ACCESS("Data Access"),
CACHE("Cache"),
GRAPHQL("GraphQL"),
MESSAGING("Messaging"),
SECURITY("Micronaut Security"),
SERVICE_DISCOVERY("Service Discovery"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package example.micronaut;

public class Author {

private final String id;
private final String username;

public Author(String id, String username) {
this.id = id;
this.username = username;
}

public String getId() {
return id;
}

public String getUsername() {
return username;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package example.micronaut;

import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import jakarta.inject.Singleton;
import org.dataloader.DataLoader;
import java.util.concurrent.CompletionStage;

@Singleton
public class AuthorDataFetcher implements DataFetcher<CompletionStage<Author>> {

@Override
public CompletionStage<Author> get(DataFetchingEnvironment environment) throws Exception {
ToDo toDo = environment.getSource();
DataLoader<String, Author> authorDataLoader = environment.getDataLoader("author");
return authorDataLoader.load(toDo.getAuthorId());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package example.micronaut;

import io.micronaut.scheduling.TaskExecutors;
import jakarta.inject.Named;
import jakarta.inject.Singleton;
import org.dataloader.MappedBatchLoader;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutorService;
import java.util.function.Function;

import static java.util.stream.Collectors.toMap;

@Singleton
public class AuthorDataLoader implements MappedBatchLoader<String, Author> {

private final AuthorRepository authorRepository;
private final ExecutorService executor;

public AuthorDataLoader(AuthorRepository authorRepository,
@Named(TaskExecutors.IO) ExecutorService executor) {
this.authorRepository = authorRepository;
this.executor = executor;
}

@Override
public CompletionStage<Map<String, Author>> load(Set<String> keys) {
return CompletableFuture.supplyAsync(() ->
authorRepository
.findAllById(keys)
.stream()
.collect(toMap(Author::getId, Function.identity())), executor);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package example.micronaut;

import jakarta.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

@Singleton
public class AuthorRepository {

private static final Logger LOG = LoggerFactory.getLogger(AuthorRepository.class);

private final Map<String, Author> authors = new HashMap<>();

public List<Author> findAllById(Collection<String> ids) {
LOG.debug("Batch loading authors: {}", ids);

return authors.values()
.stream()
.filter(it -> ids.contains(it.getId()))
.collect(Collectors.toList());
}

public Author findOrCreate(String username) {
if (!authors.containsKey(username)) {
authors.put(username, new Author(UUID.randomUUID().toString(), username));
}

return authors.get(username);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package example.micronaut;

import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import jakarta.inject.Singleton;

@Singleton
public class CompleteToDoDataFetcher implements DataFetcher<Boolean> {

private final ToDoRepository toDoRepository;

public CompleteToDoDataFetcher(ToDoRepository toDoRepository) {
this.toDoRepository = toDoRepository;
}

@Override
public Boolean get(DataFetchingEnvironment env) {
String id = env.getArgument("id");
ToDo toDo = toDoRepository.findById(id);
if (toDo != null) {
toDo.setCompleted(true);
toDoRepository.save(toDo);
return true;
} else {
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package example.micronaut;

import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import jakarta.inject.Singleton;

@Singleton
public class CreateToDoDataFetcher implements DataFetcher<ToDo> {

private final ToDoRepository toDoRepository;
private final AuthorRepository authorRepository;

public CreateToDoDataFetcher(ToDoRepository toDoRepository, AuthorRepository authorRepository) {
this.toDoRepository = toDoRepository;
this.authorRepository = authorRepository;
}

@Override
public ToDo get(DataFetchingEnvironment env) {
String title = env.getArgument("title");
String username = env.getArgument("author");
Author author = authorRepository.findOrCreate(username);
ToDo toDo = new ToDo(title, author.getId());
return toDoRepository.save(toDo);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package example.micronaut;

import io.micronaut.context.annotation.Factory;
import io.micronaut.runtime.http.scope.RequestScope;
import org.dataloader.DataLoader;
import org.dataloader.DataLoaderRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Factory
public class DataLoaderRegistryFactory {

private static final Logger LOG = LoggerFactory.getLogger(DataLoaderRegistryFactory.class);

private final AuthorDataLoader authorDataLoader;

public DataLoaderRegistryFactory(AuthorDataLoader authorDataLoader) {
this.authorDataLoader = authorDataLoader;
}

@SuppressWarnings("unused")
@RequestScope
public DataLoaderRegistry dataLoaderRegistry() {
DataLoaderRegistry dataLoaderRegistry = new DataLoaderRegistry();
dataLoaderRegistry.register("author", DataLoader.newMappedDataLoader(authorDataLoader));
LOG.trace("Created new data loader registry");
return dataLoaderRegistry;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package example.micronaut;

import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import jakarta.inject.Singleton;

@Singleton
public class DeleteToDoDataFetcher implements DataFetcher<Boolean> {

private final ToDoRepository toDoRepository;

public DeleteToDoDataFetcher(ToDoRepository toDoRepository) {
this.toDoRepository = toDoRepository;
}

@Override
public Boolean get(DataFetchingEnvironment env) {
String id = env.getArgument("id");
ToDo toDo = toDoRepository.findById(id);
if (toDo != null) {
toDoRepository.deleteById(id);
return true;
} else {
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package example.micronaut;

import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Factory;
import io.micronaut.core.io.ResourceResolver;
import jakarta.inject.Singleton;

import java.io.BufferedReader;
import java.io.InputStreamReader;

@Factory
public class GraphQLFactory {

@Bean
@Singleton
public GraphQL graphQL(ResourceResolver resourceResolver,
ToDosDataFetcher toDosDataFetcher,
CreateToDoDataFetcher createToDoDataFetcher,
CompleteToDoDataFetcher completeToDoDataFetcher,
DeleteToDoDataFetcher deleteToDoDataFetcher,
AuthorDataFetcher authorDataFetcher) {

SchemaParser schemaParser = new SchemaParser();
SchemaGenerator schemaGenerator = new SchemaGenerator();

// Parse the schema.
TypeDefinitionRegistry typeRegistry = new TypeDefinitionRegistry();
typeRegistry.merge(schemaParser.parse(new BufferedReader(new InputStreamReader(
resourceResolver.getResourceAsStream("classpath:schema.graphqls").get()))));

// Create the runtime wiring.
RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
.type("Query", typeWiring -> typeWiring
.dataFetcher("toDos", toDosDataFetcher))
.type("Mutation", typeWiring -> typeWiring
.dataFetcher("createToDo", createToDoDataFetcher)
.dataFetcher("completeToDo", completeToDoDataFetcher)
.dataFetcher("deleteToDo", deleteToDoDataFetcher))
.type("ToDo", typeWiring -> typeWiring
.dataFetcher("author", authorDataFetcher))
.build();

// Create the executable schema.
GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);

// Return the GraphQL bean.
return GraphQL.newGraphQL(graphQLSchema).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package example.micronaut;

public class ToDo {

private String id;
private String title;
private boolean completed;
private String authorId;

public ToDo(String title, String authorId) {
this.title = title;
this.authorId = authorId;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public boolean isCompleted() {
return completed;
}

public void setCompleted(boolean completed) {
this.completed = completed;
}

public String getAuthorId() {
return authorId;
}

public void setAuthorId(String authorId) {
this.authorId = authorId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package example.micronaut;

import jakarta.inject.Singleton;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;

@Singleton
public class ToDoRepository {

private final Map<String, ToDo> toDos = new LinkedHashMap<>();

public ToDoRepository(AuthorRepository authorRepository) {
save(new ToDo("Book flights to Gran Canaria", authorRepository.findOrCreate("William").getId()));
save(new ToDo("Order torrefacto coffee beans", authorRepository.findOrCreate("George").getId()));
save(new ToDo("Watch La Casa de Papel", authorRepository.findOrCreate("George").getId()));
}

public Iterable<ToDo> findAll() {
return toDos.values();
}

public ToDo findById(String id) {
return toDos.get(id);
}

public ToDo save(ToDo toDo) {
if (toDo.getId() == null) {
toDo.setId(UUID.randomUUID().toString());
}
toDos.put(toDo.getId(), toDo);
return toDo;
}

public void deleteById(String id) {
toDos.remove(id);
}
}
Loading

0 comments on commit 4e4e9f4

Please sign in to comment.