Developing GraphQL API with Netflix DGS Framework(Part-II)

I have covered basics of GraphQL API and developed AP using Netflix DGS framework to retrieve the data in Part-I of this blog post series.In the second part, I am going to cover how to develop Create and Update operations.

In GraphQL schema language , Create and Update operations are called Mutations.

Update the schema with mutation operations

type Mutation { createDepartment(department : SubmittedDepartment) :Department createEmployee(employee : SubmittedEmployee) : Employee updateEmpDepartment(emp_id: Int! , dept_id : Int!) : Boolean }
Code language: JSON / JSON with Comments (json)
https://gist.github.com/sureshgadupu/cd4376b8d12fa7a9c9aba2a2d5bf6144

In GraphQL schema, we can’t use the types defined for the queries for mutations. we will define separate types with keyword input

Define Input types

input SubmittedEmployee { first_name: String! last_name: String! gender: Gender birth_date: Date hire_date: Date deptId:Int! } input SubmittedDepartment { dept_name : String! }
Code language: JSON / JSON with Comments (json)
https://gist.github.com/sureshgadupu/8665ed757b8bfc60f245a0f8f543def6

Create corresponding Java beans

public class SubmittedDepartment { private String dept_name; ..... }
Code language: Java (java)
https://gist.github.com/sureshgadupu/36f1ac63acee7aac38f76f98da010f52
https://gist.github.com/sureshgadupu/8bb165759057e6ee9b7f20c1ec1e3476
public class SubmittedEmployee { private String first_name; private String last_name; private Gender gender; private LocalDate birth_date; private LocalDate hire_date; private Integer deptId; ..... }
Code language: Java (java)

Provide implementation methods in Netflix DGS Framework

Next, we need to provide the implementation for the mutation methods. These methods are annotated with @DgsMutation

https://gist.github.com/sureshgadupu/a8bae255b20a78da1ef588cd929df52c
@DgsComponent public class EmployeeDataFetcher { @DgsMutation public Employee createEmployee(@InputArgument SubmittedEmployee employee) { Employee emp = new Employee(emps.size() + 1, employee.getFirst_name(), employee.getLast_name(), employee.getGender(), employee.getBirth_date(), employee.getHire_date()); Optional<Department> optionalDept = departmentList.stream().filter(dept -> dept.getId() == employee.getDeptId()) .findFirst(); if (optionalDept.isPresent()) { emp.setDepartment(optionalDept.get()); } else { throw new GraphQLException("Department does not exists with id " + employee.getDeptId()); } emps.add(emp); return emp; } @DgsMutation public Department createDepartment(@InputArgument SubmittedDepartment submittedDepartment) { Department department = new Department(departmentList.size() + 1, submittedDepartment.getDept_name()); departmentList.add(department); return department; } @DgsMutation public Boolean updateEmpDepartment(@InputArgument Integer emp_id,@InputArgument Integer dept_id) { Optional<Department> optionalDept = departmentList.stream().filter(dept -> dept.getId() == dept_id) .findFirst(); Optional<Employee> optionalEmp = emps.stream().filter(emp -> emp.getId().equals(emp_id)).findFirst(); if(optionalDept.isPresent() && optionalEmp.isPresent()) { optionalEmp.get().setDepartment(optionalDept.get()); return true; } return false; } }
Code language: Java (java)

Testing GraphQL mutation operations

Now start the application and point your browser to http://localhost:8080/graphiql .

GraphQL mutations - create
GraphQL mutations - update

Writing Unit Test Cases

Now let’s write unit test cases for GraphQL API. We use the DgsQueryExecutor class to execute the queries and mutations

package com.fullstackdev.graphql.eis; import static org.assertj.core.api.Assertions.assertThat; import java.time.LocalDate; import java.util.LinkedHashMap; import java.util.List; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fullstackdev.graphql.eis.datafetcher.EmployeeDataFetcher; import com.fullstackdev.graphql.eis.entity.Department; import com.fullstackdev.graphql.eis.entity.Employee; import com.fullstackdev.graphql.eis.entity.Gender; import com.fullstackdev.graphql.eis.entity.SubmittedEmployee; import com.fullstackdev.graphql.eis.scalar.DateScalar; import com.netflix.graphql.dgs.DgsQueryExecutor; import com.netflix.graphql.dgs.autoconfig.DgsAutoConfiguration; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; import graphql.AssertException; import graphql.ExecutionResult; @SpringBootTest(classes = { DgsAutoConfiguration.class, DateScalar.class, EmployeeDataFetcher.class }) public class EmployeeDataFetcherTest { @Autowired DgsQueryExecutor dgsQueryExecutor; @MockBean EmployeeDataFetcher empDataFetcher; @BeforeEach public void before() { Department hrDepartment = new Department(1, "HR"); Department finDepartment = new Department(2, "Finance"); Department mktDepartment = new Department(3, "Marketing"); Department engDepartment = new Department(4, "Engineering"); Mockito.when(empDataFetcher.employees()) .thenAnswer(invocation -> List.of( new Employee(1, "Sally", "Bateman", Gender.M, LocalDate.of(1980, 12, 11), LocalDate.of(2014, 12, 2), hrDepartment), new Employee(2, "Jessie", "Fraser", Gender.M, LocalDate.of(1981, 11, 2), LocalDate.of(2016, 8, 12), finDepartment), new Employee(3, "Marlon", "Frost", Gender.F, LocalDate.of(1978, 12, 9), LocalDate.of(2001, 1, 20), engDepartment))); Mockito.when(empDataFetcher.employee(1)).thenAnswer(invocation -> Optional.of(new Employee(1, "Sally", "Bateman", Gender.M, LocalDate.of(1980, 12, 11), LocalDate.of(2014, 12, 2), hrDepartment))); Mockito.when(empDataFetcher.createEmployee(any(SubmittedEmployee.class))).thenAnswer(invocation -> { return new Employee(11, invocation.getArgument(0, SubmittedEmployee.class).getFirst_name(), invocation.getArgument(0, SubmittedEmployee.class).getLast_name(), invocation.getArgument(0, SubmittedEmployee.class).getGender(), invocation.getArgument(0, SubmittedEmployee.class).getBirth_date(), invocation.getArgument(0, SubmittedEmployee.class).getHire_date(), hrDepartment); }); } @Test public void test_employees() { List<String> firstNames = dgsQueryExecutor .executeAndExtractJsonPath("{ employees { id first_name last_name } }", "data.employees[*].first_name"); assertThat(firstNames).contains("Sally", "Jessie", "Marlon"); } @Test public void test_employee() { String empName = dgsQueryExecutor.executeAndExtractJsonPath("{ employee (id : 1) { id first_name last_name } }", "data.employee.last_name"); assertThat(empName).isEqualTo("Bateman"); } @Test public void test_createEmployee() throws JsonMappingException, JsonProcessingException { ExecutionResult createEmployeeResult = dgsQueryExecutor.execute( "mutation { createEmployee (employee : {first_name :\"Suresh\" , last_name : \"Gadupu\" , deptId : 1 ,gender : M , hire_date : \"2014-12-02\" , birth_date:\"1980-12-11\" }) {id first_name last_name gender hire_date birth_date } }"); assertThat(createEmployeeResult.getErrors().isEmpty()); verify(empDataFetcher).createEmployee(any(SubmittedEmployee.class)); } }
Code language: Java (java)
https://gist.github.com/sureshgadupu/cdfb03692a02a3f059a6acd1553f61f5

You can download source code for this blog post from GitHub

Other posts in this series

Similar Posts