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)
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)
Create corresponding Java beans
public class SubmittedDepartment {
private String dept_name;
.....
}
Code language: Java (java)
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
@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
.


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)
You can download source code for this blog post from GitHub
Other posts in this series