[JAVA-14660] Update code for Graphql error handling (#12839)
This commit is contained in:
@@ -9,6 +9,7 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
|
||||
|
||||
- [Getting Started with GraphQL and Spring Boot](https://www.baeldung.com/spring-graphql)
|
||||
- [Expose GraphQL Field with Different Name](https://www.baeldung.com/graphql-field-name)
|
||||
- [Error Handling in GraphQL With Spring Boot](https://www.baeldung.com/spring-graphql-error-handling)
|
||||
|
||||
### GraphQL sample queries
|
||||
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-graphql</artifactId>
|
||||
@@ -26,11 +30,21 @@
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.graphql</groupId>
|
||||
<artifactId>spring-graphql-test</artifactId>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.baeldung.graphql.error.handling;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class GraphQLErrorHandlerApplication {
|
||||
public static void main(String[] args) {
|
||||
System.setProperty("spring.profiles.default", "error-handling");
|
||||
SpringApplication.run(GraphQLErrorHandlerApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.baeldung.graphql.error.handling.controller;
|
||||
|
||||
import com.baeldung.graphql.error.handling.domain.Location;
|
||||
import com.baeldung.graphql.error.handling.domain.Vehicle;
|
||||
import com.baeldung.graphql.error.handling.service.InventoryService;
|
||||
import org.springframework.graphql.data.method.annotation.Argument;
|
||||
import org.springframework.graphql.data.method.annotation.MutationMapping;
|
||||
import org.springframework.graphql.data.method.annotation.QueryMapping;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
public class VehicleController {
|
||||
|
||||
private final InventoryService inventoryService;
|
||||
|
||||
public VehicleController(InventoryService inventoryService) {
|
||||
this.inventoryService = inventoryService;
|
||||
}
|
||||
|
||||
@QueryMapping
|
||||
public List<Vehicle> searchAll() {
|
||||
return this.inventoryService.searchAll();
|
||||
}
|
||||
|
||||
@QueryMapping
|
||||
public List<Vehicle> searchByLocation(@Argument String zipcode) {
|
||||
return this.inventoryService.searchByLocation(zipcode);
|
||||
}
|
||||
|
||||
@QueryMapping
|
||||
public Vehicle searchByVin(@Argument String vin) {
|
||||
return this.inventoryService.searchByVin(vin);
|
||||
}
|
||||
|
||||
@MutationMapping
|
||||
public Vehicle addVehicle(@Argument String vin, @Argument Integer year,
|
||||
@Argument String make, @Argument String model, @Argument String trim,
|
||||
@Argument Location location) {
|
||||
return this.inventoryService.addVehicle(vin, year, make, model, trim, location);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.baeldung.graphql.error.handling.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Location {
|
||||
@Id
|
||||
private String zipcode;
|
||||
|
||||
private String city;
|
||||
private String state;
|
||||
|
||||
@OneToMany(mappedBy = "location", fetch = FetchType.EAGER)
|
||||
private List<Vehicle> vehicles = new ArrayList<>();
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.baeldung.graphql.error.handling.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Vehicle {
|
||||
@Id
|
||||
private String vin;
|
||||
private Integer year;
|
||||
private String make;
|
||||
private String model;
|
||||
private String trim;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "fk_location")
|
||||
private Location location;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.baeldung.graphql.error.handling.exception;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AbstractGraphQLException extends RuntimeException {
|
||||
|
||||
private Map<String, Object> parameters = new HashMap<>();
|
||||
|
||||
public AbstractGraphQLException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public AbstractGraphQLException(String message, Map<String, Object> additionParams) {
|
||||
this(message);
|
||||
if (additionParams != null) {
|
||||
parameters = additionParams;
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> getExtensions() {
|
||||
return parameters.entrySet().stream()
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.baeldung.graphql.error.handling.exception;
|
||||
|
||||
import graphql.GraphQLError;
|
||||
import graphql.GraphqlErrorBuilder;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import org.springframework.graphql.execution.DataFetcherExceptionResolverAdapter;
|
||||
import org.springframework.graphql.execution.ErrorType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class CustomExceptionResolver extends DataFetcherExceptionResolverAdapter {
|
||||
|
||||
@Override
|
||||
protected GraphQLError resolveToSingleError(Throwable ex, DataFetchingEnvironment env) {
|
||||
if (ex instanceof VehicleNotFoundException) {
|
||||
return GraphqlErrorBuilder.newError()
|
||||
.errorType(ErrorType.NOT_FOUND)
|
||||
.message(ex.getMessage())
|
||||
.path(env.getExecutionStepInfo().getPath())
|
||||
.location(env.getField().getSourceLocation())
|
||||
.build();
|
||||
} else if (ex instanceof AbstractGraphQLException) {
|
||||
return GraphqlErrorBuilder.newError()
|
||||
.errorType(ErrorType.INTERNAL_ERROR)
|
||||
.message(ex.getMessage())
|
||||
.path(env.getExecutionStepInfo().getPath())
|
||||
.location(env.getField().getSourceLocation())
|
||||
.extensions(((AbstractGraphQLException) ex).getExtensions())
|
||||
.build();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.baeldung.graphql.error.handling.exception;
|
||||
|
||||
public class InvalidInputException extends RuntimeException {
|
||||
public InvalidInputException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.baeldung.graphql.error.handling.exception;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class VehicleAlreadyPresentException extends AbstractGraphQLException {
|
||||
|
||||
public VehicleAlreadyPresentException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public VehicleAlreadyPresentException(String message, Map<String, Object> additionParams) {
|
||||
super(message, additionParams);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.baeldung.graphql.error.handling.exception;
|
||||
|
||||
public class VehicleNotFoundException extends RuntimeException {
|
||||
|
||||
public VehicleNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.baeldung.graphql.error.handling.repository;
|
||||
|
||||
import com.baeldung.graphql.error.handling.domain.Vehicle;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface InventoryRepository extends JpaRepository<Vehicle, String> {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.baeldung.graphql.error.handling.repository;
|
||||
|
||||
import com.baeldung.graphql.error.handling.domain.Location;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface LocationRepository extends JpaRepository<Location, String> {
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.baeldung.graphql.error.handling.service;
|
||||
|
||||
import com.baeldung.graphql.error.handling.domain.Location;
|
||||
import com.baeldung.graphql.error.handling.domain.Vehicle;
|
||||
import com.baeldung.graphql.error.handling.exception.InvalidInputException;
|
||||
import com.baeldung.graphql.error.handling.exception.VehicleAlreadyPresentException;
|
||||
import com.baeldung.graphql.error.handling.exception.VehicleNotFoundException;
|
||||
import com.baeldung.graphql.error.handling.repository.InventoryRepository;
|
||||
import com.baeldung.graphql.error.handling.repository.LocationRepository;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
public class InventoryService {
|
||||
private final InventoryRepository inventoryRepository;
|
||||
private final LocationRepository locationRepository;
|
||||
|
||||
public InventoryService(InventoryRepository inventoryRepository, LocationRepository locationRepository) {
|
||||
this.inventoryRepository = inventoryRepository;
|
||||
this.locationRepository = locationRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Vehicle addVehicle(String vin, Integer year, String make, String model, String trim, Location location) {
|
||||
Optional<Vehicle> existingVehicle = this.inventoryRepository.findById(vin);
|
||||
if (existingVehicle.isPresent()) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("vin", vin);
|
||||
throw new VehicleAlreadyPresentException("Failed to add vehicle. Vehicle with vin already present.", params);
|
||||
}
|
||||
Vehicle vehicle = Vehicle.builder()
|
||||
.vin(vin)
|
||||
.year(year)
|
||||
.make(make)
|
||||
.model(model)
|
||||
.location(location)
|
||||
.trim(trim)
|
||||
.build();
|
||||
|
||||
this.locationRepository.save(location);
|
||||
return this.inventoryRepository.save(vehicle);
|
||||
}
|
||||
|
||||
public List<Vehicle> searchAll() {
|
||||
return this.inventoryRepository.findAll();
|
||||
}
|
||||
|
||||
public List<Vehicle> searchByLocation(String zipcode) {
|
||||
if (StringUtils.isEmpty(zipcode) || zipcode.length() != 5) {
|
||||
throw new InvalidInputException("Invalid zipcode " + zipcode + " provided.");
|
||||
}
|
||||
return this.locationRepository.findById(zipcode)
|
||||
.map(Location::getVehicles)
|
||||
.orElse(new ArrayList<>());
|
||||
}
|
||||
|
||||
public Vehicle searchByVin(String vin) {
|
||||
return this.inventoryRepository.findById(vin)
|
||||
.orElseThrow(() -> new VehicleNotFoundException("Vehicle with vin: " + vin + " not found."));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.baeldung.graphql;
|
||||
package com.baeldung.graphql.intro;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.baeldung.graphql;
|
||||
package com.baeldung.graphql.intro;
|
||||
|
||||
import org.springframework.graphql.data.method.annotation.SchemaMapping;
|
||||
import org.springframework.stereotype.Controller;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.baeldung.graphql;
|
||||
package com.baeldung.graphql.intro;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package com.baeldung.graphql;
|
||||
package com.baeldung.graphql.intro;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableAutoConfiguration(exclude = {SecurityAutoConfiguration.class})
|
||||
@EnableAutoConfiguration(exclude = {
|
||||
SecurityAutoConfiguration.class,
|
||||
HibernateJpaAutoConfiguration.class
|
||||
})
|
||||
public class GraphqlApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.baeldung.graphql;
|
||||
package com.baeldung.graphql.intro;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.baeldung.graphql;
|
||||
package com.baeldung.graphql.intro;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.baeldung.graphql;
|
||||
package com.baeldung.graphql.intro;
|
||||
|
||||
import org.springframework.graphql.data.method.annotation.Argument;
|
||||
import org.springframework.graphql.data.method.annotation.MutationMapping;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.baeldung.graphql;
|
||||
package com.baeldung.graphql.intro;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -0,0 +1,24 @@
|
||||
server:
|
||||
port: 8081
|
||||
|
||||
spring:
|
||||
graphql:
|
||||
schema:
|
||||
locations: classpath:error-handling/graphql/
|
||||
datasource:
|
||||
url: "jdbc:h2:mem:graphqldb"
|
||||
driverClassName: "org.h2.Driver"
|
||||
username: sa
|
||||
password:
|
||||
initialization-mode: always
|
||||
platform: h2
|
||||
jpa:
|
||||
show-sql: true
|
||||
properties:
|
||||
hibernate:
|
||||
dialect: org.hibernate.dialect.H2Dialect
|
||||
ddl-auto: none
|
||||
globally_quoted_identifiers: true
|
||||
|
||||
h2:
|
||||
console.enabled: true
|
||||
@@ -0,0 +1,23 @@
|
||||
type Vehicle {
|
||||
vin: ID!
|
||||
year: Int!
|
||||
make: String!
|
||||
model: String!
|
||||
trim: String!
|
||||
}
|
||||
|
||||
input Location {
|
||||
city: String
|
||||
state: String
|
||||
zipcode: String!
|
||||
}
|
||||
|
||||
type Query {
|
||||
searchAll: [Vehicle]!
|
||||
searchByLocation(zipcode: String!): [Vehicle]!
|
||||
searchByVin(vin: String!): Vehicle
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
addVehicle(vin: ID!, year: Int!, make: String!, model: String!, trim: String, location: Location): Vehicle!
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
insert into "location" values('07092', 'Mountainside', 'NJ');
|
||||
insert into "location" values ('94118', 'San Francisco', 'CA');
|
||||
insert into "location" values ('10002', 'New York', 'NY');
|
||||
|
||||
insert into "vehicle" ("vin", "year", "make", "model", "trim", "fk_location") values('KM8JN72DX7U587496', 2007, 'Hyundai', 'Tucson', null, '07092');
|
||||
insert into "vehicle" ("vin", "year", "make", "model", "trim", "fk_location") values('JTKKU4B41C1023346', 2012, 'Toyota', 'Scion', 'Xd', '94118');
|
||||
insert into "vehicle" ("vin", "year", "make", "model", "trim", "fk_location") values('1G1JC1444PZ215071', 2000, 'Chevrolet', 'CAVALIER VL', 'RS', '07092');
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.baeldung.graphql.error.handling;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.graphql.test.tester.HttpGraphQlTester;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static graphql.ErrorType.NullValueInNonNullableField;
|
||||
import static org.springframework.graphql.execution.ErrorType.INTERNAL_ERROR;
|
||||
import static org.springframework.graphql.execution.ErrorType.NOT_FOUND;
|
||||
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = GraphQLErrorHandlerApplication.class)
|
||||
@ActiveProfiles("error-handling")
|
||||
public class GraphQLErrorHandlerIntegrationTest {
|
||||
|
||||
private static final String GRAPHQL_TEST_REQUEST_PATH = "src/test/resources/graphql-files/request/%s_request.graphql";
|
||||
private static final String GRAPHQL_TEST_RESPONSE_PATH = "src/test/resources/graphql-files/response/%s_response.json";
|
||||
|
||||
@Autowired
|
||||
private HttpGraphQlTester graphQlTester;
|
||||
|
||||
@Test
|
||||
void whenMandatoryFieldNull_thenRespondWithResponseError() throws IOException {
|
||||
String nonNullFieldScenario = "non_null_field";
|
||||
|
||||
graphQlTester.document(fileToRequest(nonNullFieldScenario))
|
||||
.execute()
|
||||
.errors()
|
||||
.expect(error -> error.getErrorType() == NullValueInNonNullableField)
|
||||
.verify()
|
||||
.path("$.data")
|
||||
.matchesJson(fileToResponse(nonNullFieldScenario));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenUnhandledException_thenRespondWithGenericError() throws IOException {
|
||||
String unhandledExceptionScenario = "unhandled_exception";
|
||||
|
||||
graphQlTester.document(fileToRequest(unhandledExceptionScenario))
|
||||
.execute()
|
||||
.errors()
|
||||
.expect(error -> error.getErrorType() == INTERNAL_ERROR)
|
||||
.verify()
|
||||
.path("$.data")
|
||||
.valueIsNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHandledException_thenRespondWithCustomErrorDetails() throws IOException {
|
||||
String handledExceptionScenario = "handled_exception";
|
||||
|
||||
graphQlTester.document(fileToRequest(handledExceptionScenario))
|
||||
.execute()
|
||||
.errors()
|
||||
.expect(error -> error.getErrorType() == NOT_FOUND
|
||||
&& "Vehicle with vin: 123 not found.".equals(error.getMessage()))
|
||||
.verify()
|
||||
.path("$.data")
|
||||
.matchesJson("{\n"
|
||||
+ " \"searchByVin\": null\n"
|
||||
+ " }");
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenNoException_thenRespondWithNoError() throws IOException {
|
||||
String noExceptionScenario = "no_exception";
|
||||
|
||||
graphQlTester.document(fileToRequest(noExceptionScenario))
|
||||
.execute()
|
||||
.path("$.data")
|
||||
.matchesJson(fileToResponse(noExceptionScenario));
|
||||
}
|
||||
|
||||
private static String fileToRequest(String fileName) throws IOException {
|
||||
Path path = Paths.get(String.format(GRAPHQL_TEST_REQUEST_PATH, fileName));
|
||||
return new String(Files.readAllBytes(path));
|
||||
}
|
||||
|
||||
private static String fileToResponse(String fileName) throws IOException {
|
||||
Path path = Paths.get(String.format(GRAPHQL_TEST_RESPONSE_PATH, fileName));
|
||||
return new String(Files.readAllBytes(path));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.baeldung.graphql;
|
||||
package com.baeldung.graphql.intro;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.baeldung.graphql;
|
||||
package com.baeldung.graphql.intro;
|
||||
|
||||
import com.baeldung.graphql.intro.GraphqlApplication;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
query {
|
||||
searchByVin(vin: "123"){
|
||||
vin
|
||||
year
|
||||
make
|
||||
model
|
||||
trim
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
query {
|
||||
searchByVin(vin: "KM8JN72DX7U587496"){
|
||||
vin
|
||||
year
|
||||
make
|
||||
model
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
query {
|
||||
searchAll {
|
||||
vin
|
||||
year
|
||||
make
|
||||
model
|
||||
trim
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
query {
|
||||
searchByLocation(zipcode: "123"){
|
||||
vin
|
||||
year
|
||||
make
|
||||
model
|
||||
trim
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"searchByVin": {
|
||||
"vin": "KM8JN72DX7U587496",
|
||||
"year": 2007,
|
||||
"make": "Hyundai",
|
||||
"model": "Tucson"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"searchAll": [
|
||||
null,
|
||||
{
|
||||
"vin": "JTKKU4B41C1023346",
|
||||
"year": 2012,
|
||||
"make": "Toyota",
|
||||
"model": "Scion",
|
||||
"trim": "Xd"
|
||||
},
|
||||
{
|
||||
"vin": "1G1JC1444PZ215071",
|
||||
"year": 2000,
|
||||
"make": "Chevrolet",
|
||||
"model": "CAVALIER VL",
|
||||
"trim": "RS"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user