diff --git a/README.md b/README.md
index 8aa2e2a..9d3b885 100644
--- a/README.md
+++ b/README.md
@@ -58,3 +58,7 @@ mvn -Dallure.results.directory=/tmp/allure allure:serve
# Открыть отчёт в браузере
firefox target/site/allure-maven-plugin/index.html
```
+
+## Взаимодействие по протоколам
+Пример (REST): GET /repos/{owner}/{repo}/issues -> HTTP 200, массив объектов JSON
+Пример (GraphQL): POST /graphql -> HTTP 200, объект JSON с обязательным ключом data (или errors)
diff --git a/pom.xml b/pom.xml
index 8fdfea1..940e8d0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,6 +14,7 @@
1.18.48
3.6.0
3.16.0
+ 3.6.4
3.2.2
6.1.3
4.12.0
@@ -110,10 +111,15 @@
org.apache.maven.plugins
maven-surefire-plugin
${maven-surefire.version}
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ ${exec-maven.version}
-
- **/CleanupRunner.java
-
+ org.nokhrin.github.cleanup.CleanupRunner
+ test
diff --git a/src/main/java/org/nokhrin/github/config/Endpoints.java b/src/main/java/org/nokhrin/github/config/Endpoints.java
index e8de425..8c054c7 100644
--- a/src/main/java/org/nokhrin/github/config/Endpoints.java
+++ b/src/main/java/org/nokhrin/github/config/Endpoints.java
@@ -7,4 +7,5 @@ private Endpoints() {
public static final String REPO = "/repos/{owner}/{repo}";
public static final String ISSUES = "/repos/{owner}/{repo}/issues";
+ public static final String GRAPHQL = "/graphql";
}
diff --git a/src/test/java/org/nokhrin/github/restassured/utils/CleanupRunner.java b/src/test/java/org/nokhrin/github/cleanup/CleanupRunner.java
similarity index 58%
rename from src/test/java/org/nokhrin/github/restassured/utils/CleanupRunner.java
rename to src/test/java/org/nokhrin/github/cleanup/CleanupRunner.java
index 76f26f4..3bfb36e 100644
--- a/src/test/java/org/nokhrin/github/restassured/utils/CleanupRunner.java
+++ b/src/test/java/org/nokhrin/github/cleanup/CleanupRunner.java
@@ -1,31 +1,41 @@
-package org.nokhrin.github.restassured.utils;
+package org.nokhrin.github.cleanup;
import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.config.LogConfig;
import io.restassured.specification.RequestSpecification;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
-import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
import org.nokhrin.github.config.TestConfig;
+import org.nokhrin.github.utils.IssueUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class CleanupRunner {
- private RequestSpecification rwAuthSpec;
+ private static final Logger LOGGER = LoggerFactory.getLogger(CleanupRunner.class);
+ private static String baseUrl, owner, repo;
+ private static TestConfig config;
+ private static RequestSpecification rwAuthSpec;
+
+
+ static void main() {
+ LOGGER.debug("Clean");
+ config = TestConfig.fromEnv();
+
+ baseUrl = config.githubBaseUrl();
+ owner = config.githubOwner();
+ repo = config.githubRepo();
- @BeforeAll
- void setUp() {
- TestConfig config = TestConfig.fromEnv();
RestAssured.config = RestAssured.config()
.logConfig(LogConfig.logConfig().blacklistHeader("Authorization"));
RequestSpecification baseSpec = new RequestSpecBuilder()
- .setBaseUri(config.githubBaseUrl())
+ .setBaseUri(baseUrl)
.addHeader("Accept", "application/vnd.github+json")
- .addHeader("X-GitHub-Api-Version", "2022-11-28")
- .addHeader("User-Agent", "sqa-lab-tests/1.0")
+ .addHeader("X-GitHub-Api-Version", "2026-03-10")
+ .addHeader("User-Agent", owner)
.build();
rwAuthSpec = new RequestSpecBuilder()
@@ -34,11 +44,8 @@ void setUp() {
.addPathParam("repo", config.githubRepo())
.addHeader("Authorization", "Bearer " + config.readAndWriteToken())
.build();
- }
- @Test
- @EnabledIfSystemProperty(named = "cleanup", matches = "true")
- void runCleanup() {
+
IssueUtils.closeOpenIssues(rwAuthSpec);
}
}
diff --git a/src/test/java/org/nokhrin/github/graphql/GraphQLBaseTest.java b/src/test/java/org/nokhrin/github/graphql/GraphQLBaseTest.java
new file mode 100644
index 0000000..13aef32
--- /dev/null
+++ b/src/test/java/org/nokhrin/github/graphql/GraphQLBaseTest.java
@@ -0,0 +1,55 @@
+package org.nokhrin.github.graphql;
+
+import io.restassured.RestAssured;
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.config.LogConfig;
+import io.restassured.specification.RequestSpecification;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.TestInstance;
+import org.nokhrin.github.config.TestConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public abstract class GraphQLBaseTest {
+ protected final Logger LOGGER = LoggerFactory.getLogger(GraphQLBaseTest.class);
+
+ protected String baseUrl, owner, repo;
+ protected TestConfig config;
+ protected RequestSpecification baseSpec, unAuthSpec, rwAuthSpec, roAuthSpec;
+
+ @BeforeAll
+ void setUp() {
+ config = TestConfig.fromEnv();
+ LOGGER.debug("Загружена конфигурация: " + config);
+
+ baseUrl = config.githubBaseUrl();
+ owner = config.githubOwner();
+ repo = config.githubRepo();
+
+ RestAssured.config = RestAssured.config()
+ .logConfig(LogConfig.logConfig().blacklistHeader("Authorization"));
+
+ baseSpec = new RequestSpecBuilder()
+ .setBaseUri(baseUrl)
+ .addHeader("Accept", "application/vnd.github+json")
+ .addHeader("X-GitHub-Api-Version", "2026-03-10")
+ .addHeader("User-Agent", owner)
+ .build();
+
+ unAuthSpec = new RequestSpecBuilder()
+ .addRequestSpecification(baseSpec)
+ .build();
+
+ rwAuthSpec = new RequestSpecBuilder()
+ .addRequestSpecification(baseSpec)
+ .addHeader("Authorization", "Bearer " + config.readAndWriteToken())
+ .build();
+
+ roAuthSpec = new RequestSpecBuilder()
+ .addRequestSpecification(baseSpec)
+ .addHeader("Authorization", "Bearer " + config.readToken())
+ .build();
+
+ }
+}
diff --git a/src/test/java/org/nokhrin/github/graphql/IssueTest.java b/src/test/java/org/nokhrin/github/graphql/IssueTest.java
new file mode 100644
index 0000000..5a4d8e4
--- /dev/null
+++ b/src/test/java/org/nokhrin/github/graphql/IssueTest.java
@@ -0,0 +1,46 @@
+package org.nokhrin.github.graphql;
+
+import io.restassured.RestAssured;
+import io.restassured.http.ContentType;
+import org.junit.jupiter.api.Test;
+import org.nokhrin.github.utils.ReadResource;
+
+import java.util.Map;
+
+import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;
+import static org.hamcrest.Matchers.nullValue;
+import static org.nokhrin.github.config.Endpoints.GRAPHQL;
+
+public class IssueTest extends GraphQLBaseTest {
+
+ @Test
+ public void verifyIssuesQueryReturnsValidIssues() {
+ String query = ReadResource.readResource("graphql/get-issues.graphql");
+
+ Map variables = Map.of(
+ "owner", config.githubOwner(),
+ "name", config.githubRepo(),
+ "limit", 5
+ );
+ Map requestBody = Map.of(
+ "query", query,
+ "variables", variables
+ );
+ RestAssured.given()
+ .spec(roAuthSpec)
+ .contentType(ContentType.JSON)
+ .accept(ContentType.JSON)
+ .body(requestBody)
+ .when()
+ .post(GRAPHQL)
+ .then()
+ .statusCode(200)
+ .log().all()
+ .body("errors", nullValue())
+ .body(matchesJsonSchemaInClasspath(
+ "schemas/get-issues-response.json"
+ ))
+ ;
+ }
+
+}
diff --git a/src/test/java/org/nokhrin/github/graphql/ZenTest.java b/src/test/java/org/nokhrin/github/graphql/ZenTest.java
new file mode 100644
index 0000000..3de90af
--- /dev/null
+++ b/src/test/java/org/nokhrin/github/graphql/ZenTest.java
@@ -0,0 +1,48 @@
+package org.nokhrin.github.graphql;
+
+import io.restassured.RestAssured;
+import io.restassured.http.ContentType;
+import io.restassured.response.Response;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.nokhrin.github.utils.ReadResource;
+
+import java.util.Map;
+
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.nokhrin.github.config.Endpoints.GRAPHQL;
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class ZenTest extends GraphQLBaseTest {
+
+ @Test
+ public void verify_GraphQLendPointAvailable_tokenValid_gqlRequestValid() {
+ String query = ReadResource.readResource("graphql/healthcheck.graphql");
+
+ Response response = RestAssured.given()
+ .spec(roAuthSpec)
+ .contentType(ContentType.JSON)
+ .accept(ContentType.JSON)
+ .body(Map.of("query", query))
+ .when()
+ .post(GRAPHQL)
+ .then()
+ .statusCode(200)
+ .log().ifValidationFails()
+ .body("errors", nullValue())
+ .extract()
+ .response();
+ int limit = response.path("data.rateLimit.limit");
+ int remaining = response.path("data.rateLimit.remaining");
+ int used = response.path("data.rateLimit.used");
+
+ assertAll(
+ () -> assertTrue(limit >= 0),
+ () -> assertTrue(remaining >= 0),
+ () -> assertTrue(used >= 0),
+ () -> assertTrue(remaining <= limit),
+ () -> assertEquals(limit, remaining + used)
+ );
+ }
+}
diff --git a/src/test/java/org/nokhrin/github/restassured/AccessTest.java b/src/test/java/org/nokhrin/github/rest/AccessTest.java
similarity index 91%
rename from src/test/java/org/nokhrin/github/restassured/AccessTest.java
rename to src/test/java/org/nokhrin/github/rest/AccessTest.java
index 876a2f1..07be110 100644
--- a/src/test/java/org/nokhrin/github/restassured/AccessTest.java
+++ b/src/test/java/org/nokhrin/github/rest/AccessTest.java
@@ -1,4 +1,4 @@
-package org.nokhrin.github.restassured;
+package org.nokhrin.github.rest;
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
@@ -9,7 +9,7 @@
import static org.nokhrin.github.config.Endpoints.ISSUES;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
-public class AccessTest extends BaseTest {
+public class AccessTest extends RestBaseTest {
@Test
diff --git a/src/test/java/org/nokhrin/github/restassured/IssueTest.java b/src/test/java/org/nokhrin/github/rest/IssueTest.java
similarity index 95%
rename from src/test/java/org/nokhrin/github/restassured/IssueTest.java
rename to src/test/java/org/nokhrin/github/rest/IssueTest.java
index 92288ad..fba1c1d 100644
--- a/src/test/java/org/nokhrin/github/restassured/IssueTest.java
+++ b/src/test/java/org/nokhrin/github/rest/IssueTest.java
@@ -1,4 +1,4 @@
-package org.nokhrin.github.restassured;
+package org.nokhrin.github.rest;
import io.qameta.allure.Feature;
import io.qameta.allure.Severity;
@@ -13,9 +13,8 @@
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.nokhrin.github.model.Issue;
-import org.nokhrin.github.restassured.utils.IssueUtils;
-import org.nokhrin.github.restassured.utils.PaginationResult;
-import org.nokhrin.github.restassured.utils.WaitUtil;
+import org.nokhrin.github.utils.IssueUtils;
+import org.nokhrin.github.utils.WaitUtil;
import java.time.Duration;
import java.time.Instant;
@@ -28,10 +27,10 @@
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.nokhrin.github.config.Endpoints.ISSUES;
-import static org.nokhrin.github.restassured.utils.IssueUtils.closeOpenIssues;
+import static org.nokhrin.github.utils.IssueUtils.closeOpenIssues;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
-public class IssueTest extends BaseTest {
+public class IssueTest extends RestBaseTest {
@AfterAll
void tearDown() {
diff --git a/src/test/java/org/nokhrin/github/restassured/utils/PaginationResult.java b/src/test/java/org/nokhrin/github/rest/PaginationResult.java
similarity index 69%
rename from src/test/java/org/nokhrin/github/restassured/utils/PaginationResult.java
rename to src/test/java/org/nokhrin/github/rest/PaginationResult.java
index 7b9bfda..5879c47 100644
--- a/src/test/java/org/nokhrin/github/restassured/utils/PaginationResult.java
+++ b/src/test/java/org/nokhrin/github/rest/PaginationResult.java
@@ -1,4 +1,4 @@
-package org.nokhrin.github.restassured.utils;
+package org.nokhrin.github.rest;
import java.util.List;
diff --git a/src/test/java/org/nokhrin/github/restassured/RepoTest.java b/src/test/java/org/nokhrin/github/rest/RepoTest.java
similarity index 93%
rename from src/test/java/org/nokhrin/github/restassured/RepoTest.java
rename to src/test/java/org/nokhrin/github/rest/RepoTest.java
index 330f7d8..e55c396 100644
--- a/src/test/java/org/nokhrin/github/restassured/RepoTest.java
+++ b/src/test/java/org/nokhrin/github/rest/RepoTest.java
@@ -1,4 +1,4 @@
-package org.nokhrin.github.restassured;
+package org.nokhrin.github.rest;
import io.qameta.allure.Feature;
import io.qameta.allure.Severity;
@@ -12,7 +12,7 @@
import static org.nokhrin.github.config.Endpoints.REPO;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
-public class RepoTest extends BaseTest {
+public class RepoTest extends RestBaseTest {
@Feature("GitHub Issues API")
@Severity(SeverityLevel.CRITICAL)
diff --git a/src/test/java/org/nokhrin/github/restassured/BaseTest.java b/src/test/java/org/nokhrin/github/rest/RestBaseTest.java
similarity index 95%
rename from src/test/java/org/nokhrin/github/restassured/BaseTest.java
rename to src/test/java/org/nokhrin/github/rest/RestBaseTest.java
index 55c5233..5e7985f 100644
--- a/src/test/java/org/nokhrin/github/restassured/BaseTest.java
+++ b/src/test/java/org/nokhrin/github/rest/RestBaseTest.java
@@ -1,4 +1,4 @@
-package org.nokhrin.github.restassured;
+package org.nokhrin.github.rest;
import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
@@ -18,8 +18,8 @@
import static org.nokhrin.github.config.Endpoints.ISSUES;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
-public abstract class BaseTest {
- protected final Logger LOGGER = LoggerFactory.getLogger(BaseTest.class);
+public abstract class RestBaseTest {
+ protected final Logger LOGGER = LoggerFactory.getLogger(RestBaseTest.class);
protected String baseUrl, owner, repo;
protected TestConfig config;
diff --git a/src/test/java/org/nokhrin/github/rest/ZenTest.java b/src/test/java/org/nokhrin/github/rest/ZenTest.java
new file mode 100644
index 0000000..ad8145f
--- /dev/null
+++ b/src/test/java/org/nokhrin/github/rest/ZenTest.java
@@ -0,0 +1,36 @@
+package org.nokhrin.github.rest;
+
+import io.restassured.RestAssured;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+
+import static org.hamcrest.Matchers.emptyString;
+import static org.hamcrest.Matchers.not;
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class ZenTest extends RestBaseTest {
+
+ @Test
+ public void verifyHealthcheck200Test() {
+ RestAssured.given()
+ .spec(baseSpec)
+ .when()
+ .get("/zen")
+ .then()
+ .statusCode(200)
+ .log().ifValidationFails();
+ }
+
+ @Test
+ public void verifyHealthcheckNotEmptyBodyTest() {
+ RestAssured.given()
+ .spec(baseSpec)
+ .when()
+ .get("/zen")
+ .then()
+ .statusCode(200)
+ .body(not(emptyString()))
+ .log().ifValidationFails()
+ ;
+ }
+}
diff --git a/src/test/java/org/nokhrin/github/restassured/ZenTest.java b/src/test/java/org/nokhrin/github/restassured/ZenTest.java
deleted file mode 100644
index f039038..0000000
--- a/src/test/java/org/nokhrin/github/restassured/ZenTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-
-package org.nokhrin.github.restassured;
-
-import io.restassured.config.LogConfig;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.MethodSource;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.nokhrin.github.model.Issue;
-import org.nokhrin.github.config.TestConfig;
-import io.restassured.RestAssured;
-import io.restassured.builder.RequestSpecBuilder;
-import io.restassured.http.ContentType;
-import io.restassured.specification.RequestSpecification;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.Response;
-import org.apache.commons.lang3.RandomStringUtils;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestInstance;
-
-import java.io.IOException;
-import java.util.Map;
-import java.util.UUID;
-import java.util.stream.Stream;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.jupiter.api.Assertions.*;
-
-@TestInstance(TestInstance.Lifecycle.PER_CLASS)
-public class ZenTest extends BaseTest {
-
- @Test
- public void verifyHealthcheck200Test() {
- RestAssured.given()
- .spec(baseSpec)
- .when()
- .get("/zen")
- .then()
- .statusCode(200)
- .log().ifValidationFails();
- }
-
- @Test
- public void verifyHealthcheckNotEmptyBodyTest() {
- RestAssured.given()
- .spec(baseSpec)
- .when()
- .get("/zen")
- .then()
- .statusCode(200)
- .body(not(emptyString()))
- .log().ifValidationFails()
- ;
- }
-}
diff --git a/src/test/java/org/nokhrin/github/restassured/utils/IssueUtils.java b/src/test/java/org/nokhrin/github/utils/IssueUtils.java
similarity index 97%
rename from src/test/java/org/nokhrin/github/restassured/utils/IssueUtils.java
rename to src/test/java/org/nokhrin/github/utils/IssueUtils.java
index 32050ec..bc90418 100644
--- a/src/test/java/org/nokhrin/github/restassured/utils/IssueUtils.java
+++ b/src/test/java/org/nokhrin/github/utils/IssueUtils.java
@@ -1,9 +1,10 @@
-package org.nokhrin.github.restassured.utils;
+package org.nokhrin.github.utils;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
+import org.nokhrin.github.rest.PaginationResult;
import java.time.Instant;
import java.util.List;
diff --git a/src/test/java/org/nokhrin/github/utils/ReadResource.java b/src/test/java/org/nokhrin/github/utils/ReadResource.java
new file mode 100644
index 0000000..ca7591e
--- /dev/null
+++ b/src/test/java/org/nokhrin/github/utils/ReadResource.java
@@ -0,0 +1,22 @@
+package org.nokhrin.github.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
+
+public class ReadResource {
+ private ReadResource() {
+ }
+
+ public static String readResource(String resourcePath) {
+ try (InputStream is = ReadResource.class.getClassLoader().getResourceAsStream(resourcePath)) {
+ if (is == null) {
+ throw new IllegalArgumentException("Ресурс не найден: " + resourcePath);
+ }
+ return new String(is.readAllBytes(), StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+}
diff --git a/src/test/java/org/nokhrin/github/restassured/utils/WaitUtil.java b/src/test/java/org/nokhrin/github/utils/WaitUtil.java
similarity index 94%
rename from src/test/java/org/nokhrin/github/restassured/utils/WaitUtil.java
rename to src/test/java/org/nokhrin/github/utils/WaitUtil.java
index 9420f91..501ea38 100644
--- a/src/test/java/org/nokhrin/github/restassured/utils/WaitUtil.java
+++ b/src/test/java/org/nokhrin/github/utils/WaitUtil.java
@@ -1,10 +1,9 @@
-package org.nokhrin.github.restassured.utils;
+package org.nokhrin.github.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.Duration;
-import java.time.Instant;
import java.util.function.Supplier;
public class WaitUtil {
diff --git a/src/test/resources/graphql/get-issues.graphql b/src/test/resources/graphql/get-issues.graphql
new file mode 100644
index 0000000..c6192aa
--- /dev/null
+++ b/src/test/resources/graphql/get-issues.graphql
@@ -0,0 +1,8 @@
+query GetIssues($owner: String!, $name: String!, $limit: Int!) {
+ repository(owner: $owner, name: $name) {
+ issues(first: $limit, orderBy: {field: CREATED_AT, direction: DESC}) {
+ nodes { number title state }
+ pageInfo { hasNextPage endCursor }
+ }
+ }
+}
diff --git a/src/test/resources/graphql/healthcheck.graphql b/src/test/resources/graphql/healthcheck.graphql
new file mode 100644
index 0000000..48e1d22
--- /dev/null
+++ b/src/test/resources/graphql/healthcheck.graphql
@@ -0,0 +1,7 @@
+query {
+ rateLimit {
+ limit
+ remaining
+ used
+ }
+}
diff --git a/src/test/resources/schemas/get-issues-response.json b/src/test/resources/schemas/get-issues-response.json
new file mode 100644
index 0000000..a04ee24
--- /dev/null
+++ b/src/test/resources/schemas/get-issues-response.json
@@ -0,0 +1,97 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "type": "object",
+ "required": [
+ "data"
+ ],
+ "properties": {
+ "data": {
+ "type": "object",
+ "required": [
+ "repository"
+ ],
+ "properties": {
+ "repository": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "required": [
+ "issues"
+ ],
+ "properties": {
+ "issues": {
+ "type": "object",
+ "required": [
+ "nodes",
+ "pageInfo"
+ ],
+ "properties": {
+ "nodes": {
+ "type": "array",
+ "items": {
+ "$ref": "#/$defs/issue"
+ }
+ },
+ "pageInfo": {
+ "$ref": "#/$defs/pageInfo"
+ }
+ },
+ "additionalProperties": false
+ }
+ },
+ "additionalProperties": false
+ }
+ },
+ "additionalProperties": false
+ }
+ },
+ "$defs": {
+ "issue": {
+ "type": "object",
+ "required": [
+ "number",
+ "title",
+ "state"
+ ],
+ "properties": {
+ "number": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1
+ },
+ "state": {
+ "type": "string",
+ "enum": [
+ "OPEN",
+ "CLOSED"
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "pageInfo": {
+ "type": "object",
+ "required": [
+ "hasNextPage",
+ "endCursor"
+ ],
+ "properties": {
+ "hasNextPage": {
+ "type": "boolean"
+ },
+ "endCursor": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ },
+ "additionalProperties": false
+}