diff --git a/changes.md b/changes.md
index 0fb00c35f92..107e80c15e4 100644
--- a/changes.md
+++ b/changes.md
@@ -75,9 +75,29 @@
**History changes**
+
+Added Resource(s)
+
+- added resource `/{projectKey}/graphql`
+
+
+
+
+Added Method(s)
+
+- added method `$apiRoot->withProjectKeyValue()->graphql()->post()`
+
+
+
Added Type(s)
+- added type `GraphQLRequest`
+- added type `GraphQLResponse`
+- added type `GraphQLError`
+- added type `GraphQLErrorLocation`
+- added type `GraphQLVariablesMap`
+- added type `GraphQLErrorObject`
- added type `ChangeTargetPatternChangeValue`
- added type `PatternComponent`
diff --git a/lib/commercetools-history-tests/test/unit/Client/Resource/ResourceByProjectKeyGraphqlTest.php b/lib/commercetools-history-tests/test/unit/Client/Resource/ResourceByProjectKeyGraphqlTest.php
new file mode 100644
index 00000000000..34a6dfd21dc
--- /dev/null
+++ b/lib/commercetools-history-tests/test/unit/Client/Resource/ResourceByProjectKeyGraphqlTest.php
@@ -0,0 +1,149 @@
+assertSame(strtolower($method), strtolower($request->getMethod()));
+ $this->assertSame($relativeUri, (string) $request->getUri());
+ if (!is_null($body)) {
+ $this->assertJsonStringEqualsJsonString($body, (string) $request->getBody());
+ } else {
+ $this->assertSame("", (string) $request->getBody());
+ }
+ }
+
+
+
+ /**
+ * @dataProvider getRequestBuilderResponses()
+ */
+ public function testMapFromResponse(callable $builderFunction, $statusCode)
+ {
+ $builder = new HistoryRequestBuilder();
+ $request = $builderFunction($builder);
+ $this->assertInstanceOf(ApiRequest::class, $request);
+
+ $response = new Response($statusCode, [], "{}");
+ $this->assertInstanceOf(JsonObject::class, $request->mapFromResponse($response));
+ }
+
+ /**
+ * @dataProvider getRequestBuilders()
+ */
+ public function testExecuteClientException(callable $builderFunction)
+ {
+ $client = $this->createMock(ClientInterface::class);
+
+ $builder = new HistoryRequestBuilder($client);
+ $request = $builderFunction($builder);
+ $client->method("send")->willThrowException(new ClientException("Oops!", $request, new Response(400)));
+
+ $this->expectException(ApiClientException::class);
+ $request->execute();
+ }
+
+ /**
+ * @dataProvider getRequestBuilders()
+ */
+ public function testExecuteServerException(callable $builderFunction)
+ {
+ $client = $this->createMock(ClientInterface::class);
+
+ $builder = new HistoryRequestBuilder($client);
+ $request = $builderFunction($builder);
+ $client->method("send")->willThrowException(new ServerException("Oops!", $request, new Response(500)));
+
+ $this->expectException(ApiServerException::class);
+ $request->execute();
+ }
+
+ public function getRequests()
+ {
+ return [
+ 'ByProjectKeyGraphqlPost' => [
+ function (HistoryRequestBuilder $builder): RequestInterface {
+ return $builder
+ ->withProjectKeyValue("test_projectKey")
+ ->graphql()
+ ->post(null);
+ },
+ 'post',
+ 'test_projectKey/graphql',
+ ]
+ ];
+ }
+
+ public function getResources()
+ {
+ return [
+ ];
+ }
+
+ public function getRequestBuilders()
+ {
+ return [
+ 'ByProjectKeyGraphqlPost' => [
+ function (HistoryRequestBuilder $builder): RequestInterface {
+ return $builder
+ ->withProjectKeyValue("projectKey")
+ ->graphql()
+ ->post(null);
+ }
+ ]
+ ];
+ }
+
+ public function getRequestBuilderResponses()
+ {
+ return [
+ 'ByProjectKeyGraphqlPost_200' => [
+ function (HistoryRequestBuilder $builder): RequestInterface {
+ return $builder
+ ->withProjectKeyValue("projectKey")
+ ->graphql()
+ ->post(null);
+ },
+ 200
+ ],
+ 'ByProjectKeyGraphqlPost_599' => [
+ function (HistoryRequestBuilder $builder): RequestInterface {
+ return $builder
+ ->withProjectKeyValue("projectKey")
+ ->graphql()
+ ->post(null);
+ },
+ 599
+ ]
+ ];
+ }
+}
diff --git a/lib/commercetools-history-tests/test/unit/Client/Resource/ResourceByProjectKeyTest.php b/lib/commercetools-history-tests/test/unit/Client/Resource/ResourceByProjectKeyTest.php
index 69e16f92fa0..53a4859e513 100644
--- a/lib/commercetools-history-tests/test/unit/Client/Resource/ResourceByProjectKeyTest.php
+++ b/lib/commercetools-history-tests/test/unit/Client/Resource/ResourceByProjectKeyTest.php
@@ -19,6 +19,7 @@
use GuzzleHttp\Exception\ServerException;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Psr7\Response;
+use Commercetools\History\Client\Resource\ResourceByProjectKeyGraphql;
use Commercetools\History\Client\Resource\ResourceByProjectKeyByResourceType;
/**
@@ -295,6 +296,16 @@ function (HistoryRequestBuilder $builder): RequestInterface {
public function getResources()
{
return [
+ 'ResourceByProjectKeyGraphql' => [
+ function (HistoryRequestBuilder $builder): ResourceByProjectKeyGraphql {
+ return $builder
+ ->withProjectKeyValue("test_projectKey")
+ ->graphql();
+ },
+ ResourceByProjectKeyGraphql::class,
+ ['projectKey' => 'test_projectKey'],
+ '/{projectKey}/graphql'
+ ],
'ResourceByProjectKeyByResourceType' => [
function (HistoryRequestBuilder $builder): ResourceByProjectKeyByResourceType {
return $builder
diff --git a/lib/commercetools-history/docs/RequestBuilder.md b/lib/commercetools-history/docs/RequestBuilder.md
index 6f8b6e98d7c..393dd9f4c2f 100644
--- a/lib/commercetools-history/docs/RequestBuilder.md
+++ b/lib/commercetools-history/docs/RequestBuilder.md
@@ -50,3 +50,17 @@ $request = $builder
->withIDValue("ID")
->get();
```
+## `withProjectKeyValue("projectKey")->graphql()->post(null)`
+
+Execute a GraphQL request.
+
+### Example
+```php
+use Commercetools\History\Client\HistoryRequestBuilder;
+
+$builder = new HistoryRequestBuilder();
+$request = $builder
+ ->withProjectKeyValue("projectKey")
+ ->graphql()
+ ->post(null);
+```
diff --git a/lib/commercetools-history/src/Client/Resource/ByProjectKeyGraphqlPost.php b/lib/commercetools-history/src/Client/Resource/ByProjectKeyGraphqlPost.php
new file mode 100644
index 00000000000..4c04ee73c1c
--- /dev/null
+++ b/lib/commercetools-history/src/Client/Resource/ByProjectKeyGraphqlPost.php
@@ -0,0 +1,119 @@
+ $headers
+ */
+ public function __construct(string $projectKey, $body = null, array $headers = [], ClientInterface $client = null)
+ {
+ $uri = str_replace(['{projectKey}'], [$projectKey], '{projectKey}/graphql');
+ parent::__construct($client, 'POST', $uri, $headers, is_object($body) || is_array($body) ? json_encode($body) : $body);
+ }
+
+ /**
+ * @template T of JsonObject
+ * @psalm-param ?class-string $resultType
+ * @return GraphQLResponse|JsonObject|T|null
+ */
+ public function mapFromResponse(?ResponseInterface $response, string $resultType = null)
+ {
+ if (is_null($response)) {
+ return null;
+ }
+ if (is_null($resultType)) {
+ switch ($response->getStatusCode()) {
+ case '200':
+ $resultType = GraphQLResponseModel::class;
+
+ break;
+ default:
+ $resultType = JsonObjectModel::class;
+
+ break;
+ }
+ }
+
+ return $resultType::of($this->responseData($response));
+ }
+
+ /**
+ * @template T of JsonObject
+ * @psalm-param ?class-string $resultType
+ *
+ * @return null|T|GraphQLResponse|JsonObject
+ */
+ public function execute(array $options = [], string $resultType = null)
+ {
+ try {
+ $response = $this->send($options);
+ } catch (ServerException $e) {
+ $response = $e->getResponse();
+ $e = ExceptionFactory::createServerException($e, $this, $response, $this->mapFromResponse($response, $resultType));
+ throw $e;
+ } catch (ClientException $e) {
+ $response = $e->getResponse();
+ $e = ExceptionFactory::createClientException($e, $this, $response, $this->mapFromResponse($response, $resultType));
+ throw $e;
+ }
+
+ return $this->mapFromResponse($response, $resultType);
+ }
+
+ /**
+ * @template T of JsonObject
+ * @psalm-param ?class-string $resultType
+ *
+ * @return PromiseInterface
+ */
+ public function executeAsync(array $options = [], string $resultType = null)
+ {
+ return $this->sendAsync($options)->then(
+ function(ResponseInterface $response) use ($resultType) {
+ return $this->mapFromResponse($response, $resultType);
+ },
+ function (RequestException $e) use ($resultType) {
+ $response = $e->getResponse();
+ if ($e instanceof ServerException) {
+ $e = ExceptionFactory::createServerException($e, $this, $response, $this->mapFromResponse($response, $resultType));
+ }
+ if ($e instanceof ClientException) {
+ $e = ExceptionFactory::createClientException($e, $this, $response, $this->mapFromResponse($response, $resultType));
+ }
+ throw $e;
+ }
+ );
+ }
+
+}
diff --git a/lib/commercetools-history/src/Client/Resource/ResourceByProjectKey.php b/lib/commercetools-history/src/Client/Resource/ResourceByProjectKey.php
index f3717ab4353..f3e9c2f027c 100644
--- a/lib/commercetools-history/src/Client/Resource/ResourceByProjectKey.php
+++ b/lib/commercetools-history/src/Client/Resource/ResourceByProjectKey.php
@@ -24,6 +24,14 @@ public function __construct(array $args = [], ClientInterface $client = null) {
parent::__construct('/{projectKey}', $args, $client);
}
+ /**
+ */
+ public function graphql(): ResourceByProjectKeyGraphql
+ {
+ $args = $this->getArgs();
+
+ return new ResourceByProjectKeyGraphql($args, $this->getClient());
+ }
/**
*/
public function withResourceTypeValue(string $resourceType = null): ResourceByProjectKeyByResourceType
diff --git a/lib/commercetools-history/src/Client/Resource/ResourceByProjectKeyGraphql.php b/lib/commercetools-history/src/Client/Resource/ResourceByProjectKeyGraphql.php
new file mode 100644
index 00000000000..30e8c9ee51d
--- /dev/null
+++ b/lib/commercetools-history/src/Client/Resource/ResourceByProjectKeyGraphql.php
@@ -0,0 +1,39 @@
+ $args
+ */
+ public function __construct(array $args = [], ClientInterface $client = null) {
+ parent::__construct('/{projectKey}/graphql', $args, $client);
+ }
+
+ /**
+ * @psalm-param ?GraphQLRequest $body
+ * @psalm-param array $headers
+ */
+ public function post(?GraphQLRequest $body = null, array $headers = []): ByProjectKeyGraphqlPost
+ {
+ $args = $this->getArgs();
+
+ return new ByProjectKeyGraphqlPost($args['projectKey'], $body, $headers, $this->getClient());
+ }
+
+}
diff --git a/lib/commercetools-history/src/Models/Error/GraphQLErrorObject.php b/lib/commercetools-history/src/Models/Error/GraphQLErrorObject.php
new file mode 100644
index 00000000000..156108a46eb
--- /dev/null
+++ b/lib/commercetools-history/src/Models/Error/GraphQLErrorObject.php
@@ -0,0 +1,28 @@
+One of the error codes that is listed on the Errors page.
+ *
+
+ * @return null|string
+ */
+ public function getCode();
+
+}
diff --git a/lib/commercetools-history/src/Models/Error/GraphQLErrorObjectBuilder.php b/lib/commercetools-history/src/Models/Error/GraphQLErrorObjectBuilder.php
new file mode 100644
index 00000000000..8e57eb2f89b
--- /dev/null
+++ b/lib/commercetools-history/src/Models/Error/GraphQLErrorObjectBuilder.php
@@ -0,0 +1,37 @@
+
+ */
+final class GraphQLErrorObjectBuilder implements Builder
+{
+
+
+
+
+ public function build(): GraphQLErrorObject
+ {
+ return new GraphQLErrorObjectModel(
+ );
+ }
+
+ public static function of(): GraphQLErrorObjectBuilder
+ {
+ return new self();
+ }
+}
diff --git a/lib/commercetools-history/src/Models/Error/GraphQLErrorObjectCollection.php b/lib/commercetools-history/src/Models/Error/GraphQLErrorObjectCollection.php
new file mode 100644
index 00000000000..e9b01fdca05
--- /dev/null
+++ b/lib/commercetools-history/src/Models/Error/GraphQLErrorObjectCollection.php
@@ -0,0 +1,56 @@
+
+ * @method GraphQLErrorObject current()
+ * @method GraphQLErrorObject end()
+ * @method GraphQLErrorObject at($offset)
+ */
+class GraphQLErrorObjectCollection extends MapperSequence
+{
+ /**
+ * @psalm-assert GraphQLErrorObject $value
+ * @psalm-param GraphQLErrorObject|stdClass $value
+ * @throws InvalidArgumentException
+ *
+ * @return GraphQLErrorObjectCollection
+ */
+ public function add($value)
+ {
+ if (!$value instanceof GraphQLErrorObject) {
+ throw new InvalidArgumentException();
+ }
+ $this->store($value);
+
+ return $this;
+ }
+
+ /**
+ * @psalm-return callable(int):?GraphQLErrorObject
+ */
+ protected function mapper()
+ {
+ return function (?int $index): ?GraphQLErrorObject {
+ $data = $this->get($index);
+ if ($data instanceof stdClass) {
+ /** @var GraphQLErrorObject $data */
+ $data = GraphQLErrorObjectModel::of($data);
+ $this->set($data, $index);
+ }
+
+ return $data;
+ };
+ }
+}
diff --git a/lib/commercetools-history/src/Models/Error/GraphQLErrorObjectModel.php b/lib/commercetools-history/src/Models/Error/GraphQLErrorObjectModel.php
new file mode 100644
index 00000000000..618c4eb622b
--- /dev/null
+++ b/lib/commercetools-history/src/Models/Error/GraphQLErrorObjectModel.php
@@ -0,0 +1,113 @@
+ >
+ *
+ */
+ private static $discriminatorClasses = [
+ ];
+
+ /**
+ * @psalm-suppress MissingParamType
+ */
+ public function __construct(
+ ?string $code = null
+ ) {
+ $this->code = $code;
+
+ }
+
+ /**
+ * One of the error codes that is listed on the Errors page.
+ *
+ *
+ * @return null|string
+ */
+ public function getCode()
+ {
+ if (is_null($this->code)) {
+ /** @psalm-var ?string $data */
+ $data = $this->raw(self::FIELD_CODE);
+ if (is_null($data)) {
+ return null;
+ }
+ $this->code = (string) $data;
+ }
+
+ return $this->code;
+ }
+
+
+
+ /**
+ * @return mixed
+ */
+ public function by(string $key)
+ {
+ $data = $this->raw($key);
+ if (is_null($data)) {
+ return null;
+ }
+ if (preg_match(GraphQLErrorObject::FIELD_PATTERN1, $key) === 1) {
+ /** @psalm-var stdClass $data */
+ return JsonObjectModel::of($data);
+ }
+
+ return $data;
+ }
+
+
+ /**
+ * @psalm-param stdClass|array $value
+ * @psalm-return class-string
+ */
+ public static function resolveDiscriminatorClass($value): string
+ {
+ $fieldName = GraphQLErrorObject::DISCRIMINATOR_FIELD;
+ if (is_object($value) && isset($value->$fieldName)) {
+ /** @psalm-var string $discriminatorValue */
+ $discriminatorValue = $value->$fieldName;
+ if (isset(self::$discriminatorClasses[$discriminatorValue])) {
+ return self::$discriminatorClasses[$discriminatorValue];
+ }
+ }
+ if (is_array($value) && isset($value[$fieldName])) {
+ /** @psalm-var string $discriminatorValue */
+ $discriminatorValue = $value[$fieldName];
+ if (isset(self::$discriminatorClasses[$discriminatorValue])) {
+ return self::$discriminatorClasses[$discriminatorValue];
+ }
+ }
+
+ /** @psalm-var class-string */
+ $type = GraphQLErrorObjectModel::class;
+ return $type;
+ }
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLError.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLError.php
new file mode 100644
index 00000000000..ee06bfabbfb
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLError.php
@@ -0,0 +1,74 @@
+Detailed description of the error explaining the root cause of the problem and suggesting how to correct the error.
+ *
+
+ * @return null|string
+ */
+ public function getMessage();
+
+ /**
+ * Location within your query where the error occurred.
+ *
+
+ * @return null|GraphQLErrorLocationCollection
+ */
+ public function getLocations();
+
+ /**
+ * Query fields listed in order from the root of the query response up to the field in which the error occurred. path
is displayed in the response only if an error is associated with a particular field in the query result.
+ *
+
+ * @return null|array
+ */
+ public function getPath();
+
+ /**
+ * Dictionary with additional information where applicable.
+ *
+
+ * @return null|GraphQLErrorObject
+ */
+ public function getExtensions();
+
+ /**
+ * @param ?string $message
+ */
+ public function setMessage(?string $message): void;
+
+ /**
+ * @param ?GraphQLErrorLocationCollection $locations
+ */
+ public function setLocations(?GraphQLErrorLocationCollection $locations): void;
+
+ /**
+ * @param ?array $path
+ */
+ public function setPath(?array $path): void;
+
+ /**
+ * @param ?GraphQLErrorObject $extensions
+ */
+ public function setExtensions(?GraphQLErrorObject $extensions): void;
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorBuilder.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorBuilder.php
new file mode 100644
index 00000000000..95278d33a16
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorBuilder.php
@@ -0,0 +1,162 @@
+
+ */
+final class GraphQLErrorBuilder implements Builder
+{
+ /**
+
+ * @var ?string
+ */
+ private $message;
+
+ /**
+
+ * @var ?GraphQLErrorLocationCollection
+ */
+ private $locations;
+
+ /**
+
+ * @var ?array
+ */
+ private $path;
+
+ /**
+
+ * @var null|GraphQLErrorObject|GraphQLErrorObjectBuilder
+ */
+ private $extensions;
+
+ /**
+ * Detailed description of the error explaining the root cause of the problem and suggesting how to correct the error.
+ *
+
+ * @return null|string
+ */
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ /**
+ * Location within your query where the error occurred.
+ *
+
+ * @return null|GraphQLErrorLocationCollection
+ */
+ public function getLocations()
+ {
+ return $this->locations;
+ }
+
+ /**
+ * Query fields listed in order from the root of the query response up to the field in which the error occurred. path
is displayed in the response only if an error is associated with a particular field in the query result.
+ *
+
+ * @return null|array
+ */
+ public function getPath()
+ {
+ return $this->path;
+ }
+
+ /**
+ * Dictionary with additional information where applicable.
+ *
+
+ * @return null|GraphQLErrorObject
+ */
+ public function getExtensions()
+ {
+ return $this->extensions instanceof GraphQLErrorObjectBuilder ? $this->extensions->build() : $this->extensions;
+ }
+
+ /**
+ * @param ?string $message
+ * @return $this
+ */
+ public function withMessage(?string $message)
+ {
+ $this->message = $message;
+
+ return $this;
+ }
+
+ /**
+ * @param ?GraphQLErrorLocationCollection $locations
+ * @return $this
+ */
+ public function withLocations(?GraphQLErrorLocationCollection $locations)
+ {
+ $this->locations = $locations;
+
+ return $this;
+ }
+
+ /**
+ * @param ?array $path
+ * @return $this
+ */
+ public function withPath(?array $path)
+ {
+ $this->path = $path;
+
+ return $this;
+ }
+
+ /**
+ * @param ?GraphQLErrorObject $extensions
+ * @return $this
+ */
+ public function withExtensions(?GraphQLErrorObject $extensions)
+ {
+ $this->extensions = $extensions;
+
+ return $this;
+ }
+
+ /**
+ * @deprecated use withExtensions() instead
+ * @return $this
+ */
+ public function withExtensionsBuilder(?GraphQLErrorObjectBuilder $extensions)
+ {
+ $this->extensions = $extensions;
+
+ return $this;
+ }
+
+ public function build(): GraphQLError
+ {
+ return new GraphQLErrorModel(
+ $this->message,
+ $this->locations,
+ $this->path,
+ $this->extensions instanceof GraphQLErrorObjectBuilder ? $this->extensions->build() : $this->extensions
+ );
+ }
+
+ public static function of(): GraphQLErrorBuilder
+ {
+ return new self();
+ }
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorCollection.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorCollection.php
new file mode 100644
index 00000000000..947ce31864f
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorCollection.php
@@ -0,0 +1,56 @@
+
+ * @method GraphQLError current()
+ * @method GraphQLError end()
+ * @method GraphQLError at($offset)
+ */
+class GraphQLErrorCollection extends MapperSequence
+{
+ /**
+ * @psalm-assert GraphQLError $value
+ * @psalm-param GraphQLError|stdClass $value
+ * @throws InvalidArgumentException
+ *
+ * @return GraphQLErrorCollection
+ */
+ public function add($value)
+ {
+ if (!$value instanceof GraphQLError) {
+ throw new InvalidArgumentException();
+ }
+ $this->store($value);
+
+ return $this;
+ }
+
+ /**
+ * @psalm-return callable(int):?GraphQLError
+ */
+ protected function mapper()
+ {
+ return function (?int $index): ?GraphQLError {
+ $data = $this->get($index);
+ if ($data instanceof stdClass) {
+ /** @var GraphQLError $data */
+ $data = GraphQLErrorModel::of($data);
+ $this->set($data, $index);
+ }
+
+ return $data;
+ };
+ }
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorLocation.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorLocation.php
new file mode 100644
index 00000000000..9d2bd2e5f46
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorLocation.php
@@ -0,0 +1,45 @@
+Line number of the query where the error occurred.
+ *
+
+ * @return null|int
+ */
+ public function getLine();
+
+ /**
+ * Position in line
where the error occurred.
+ *
+
+ * @return null|int
+ */
+ public function getColumn();
+
+ /**
+ * @param ?int $line
+ */
+ public function setLine(?int $line): void;
+
+ /**
+ * @param ?int $column
+ */
+ public function setColumn(?int $column): void;
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorLocationBuilder.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorLocationBuilder.php
new file mode 100644
index 00000000000..e6dcc9f5139
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorLocationBuilder.php
@@ -0,0 +1,92 @@
+
+ */
+final class GraphQLErrorLocationBuilder implements Builder
+{
+ /**
+
+ * @var ?int
+ */
+ private $line;
+
+ /**
+
+ * @var ?int
+ */
+ private $column;
+
+ /**
+ * Line number of the query where the error occurred.
+ *
+
+ * @return null|int
+ */
+ public function getLine()
+ {
+ return $this->line;
+ }
+
+ /**
+ * Position in line
where the error occurred.
+ *
+
+ * @return null|int
+ */
+ public function getColumn()
+ {
+ return $this->column;
+ }
+
+ /**
+ * @param ?int $line
+ * @return $this
+ */
+ public function withLine(?int $line)
+ {
+ $this->line = $line;
+
+ return $this;
+ }
+
+ /**
+ * @param ?int $column
+ * @return $this
+ */
+ public function withColumn(?int $column)
+ {
+ $this->column = $column;
+
+ return $this;
+ }
+
+
+ public function build(): GraphQLErrorLocation
+ {
+ return new GraphQLErrorLocationModel(
+ $this->line,
+ $this->column
+ );
+ }
+
+ public static function of(): GraphQLErrorLocationBuilder
+ {
+ return new self();
+ }
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorLocationCollection.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorLocationCollection.php
new file mode 100644
index 00000000000..8edd7bb5fb7
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorLocationCollection.php
@@ -0,0 +1,56 @@
+
+ * @method GraphQLErrorLocation current()
+ * @method GraphQLErrorLocation end()
+ * @method GraphQLErrorLocation at($offset)
+ */
+class GraphQLErrorLocationCollection extends MapperSequence
+{
+ /**
+ * @psalm-assert GraphQLErrorLocation $value
+ * @psalm-param GraphQLErrorLocation|stdClass $value
+ * @throws InvalidArgumentException
+ *
+ * @return GraphQLErrorLocationCollection
+ */
+ public function add($value)
+ {
+ if (!$value instanceof GraphQLErrorLocation) {
+ throw new InvalidArgumentException();
+ }
+ $this->store($value);
+
+ return $this;
+ }
+
+ /**
+ * @psalm-return callable(int):?GraphQLErrorLocation
+ */
+ protected function mapper()
+ {
+ return function (?int $index): ?GraphQLErrorLocation {
+ $data = $this->get($index);
+ if ($data instanceof stdClass) {
+ /** @var GraphQLErrorLocation $data */
+ $data = GraphQLErrorLocationModel::of($data);
+ $this->set($data, $index);
+ }
+
+ return $data;
+ };
+ }
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorLocationModel.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorLocationModel.php
new file mode 100644
index 00000000000..bb9a2cdce79
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorLocationModel.php
@@ -0,0 +1,108 @@
+line = $line;
+ $this->column = $column;
+
+ }
+
+ /**
+ * Line number of the query where the error occurred.
+ *
+ *
+ * @return null|int
+ */
+ public function getLine()
+ {
+ if (is_null($this->line)) {
+ /** @psalm-var ?int $data */
+ $data = $this->raw(self::FIELD_LINE);
+ if (is_null($data)) {
+ return null;
+ }
+ $this->line = (int) $data;
+ }
+
+ return $this->line;
+ }
+
+ /**
+ * Position in line
where the error occurred.
+ *
+ *
+ * @return null|int
+ */
+ public function getColumn()
+ {
+ if (is_null($this->column)) {
+ /** @psalm-var ?int $data */
+ $data = $this->raw(self::FIELD_COLUMN);
+ if (is_null($data)) {
+ return null;
+ }
+ $this->column = (int) $data;
+ }
+
+ return $this->column;
+ }
+
+
+ /**
+ * @param ?int $line
+ */
+ public function setLine(?int $line): void
+ {
+ $this->line = $line;
+ }
+
+ /**
+ * @param ?int $column
+ */
+ public function setColumn(?int $column): void
+ {
+ $this->column = $column;
+ }
+
+
+
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorModel.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorModel.php
new file mode 100644
index 00000000000..090b2f8d31b
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLErrorModel.php
@@ -0,0 +1,183 @@
+message = $message;
+ $this->locations = $locations;
+ $this->path = $path;
+ $this->extensions = $extensions;
+
+ }
+
+ /**
+ * Detailed description of the error explaining the root cause of the problem and suggesting how to correct the error.
+ *
+ *
+ * @return null|string
+ */
+ public function getMessage()
+ {
+ if (is_null($this->message)) {
+ /** @psalm-var ?string $data */
+ $data = $this->raw(self::FIELD_MESSAGE);
+ if (is_null($data)) {
+ return null;
+ }
+ $this->message = (string) $data;
+ }
+
+ return $this->message;
+ }
+
+ /**
+ * Location within your query where the error occurred.
+ *
+ *
+ * @return null|GraphQLErrorLocationCollection
+ */
+ public function getLocations()
+ {
+ if (is_null($this->locations)) {
+ /** @psalm-var ?list $data */
+ $data = $this->raw(self::FIELD_LOCATIONS);
+ if (is_null($data)) {
+ return null;
+ }
+ $this->locations = GraphQLErrorLocationCollection::fromArray($data);
+ }
+
+ return $this->locations;
+ }
+
+ /**
+ * Query fields listed in order from the root of the query response up to the field in which the error occurred. path
is displayed in the response only if an error is associated with a particular field in the query result.
+ *
+ *
+ * @return null|array
+ */
+ public function getPath()
+ {
+ if (is_null($this->path)) {
+ /** @psalm-var ?list $data */
+ $data = $this->raw(self::FIELD_PATH);
+ if (is_null($data)) {
+ return null;
+ }
+ $this->path = $data;
+ }
+
+ return $this->path;
+ }
+
+ /**
+ * Dictionary with additional information where applicable.
+ *
+ *
+ * @return null|GraphQLErrorObject
+ */
+ public function getExtensions()
+ {
+ if (is_null($this->extensions)) {
+ /** @psalm-var stdClass|array|null $data */
+ $data = $this->raw(self::FIELD_EXTENSIONS);
+ if (is_null($data)) {
+ return null;
+ }
+ $className = GraphQLErrorObjectModel::resolveDiscriminatorClass($data);
+ $this->extensions = $className::of($data);
+ }
+
+ return $this->extensions;
+ }
+
+
+ /**
+ * @param ?string $message
+ */
+ public function setMessage(?string $message): void
+ {
+ $this->message = $message;
+ }
+
+ /**
+ * @param ?GraphQLErrorLocationCollection $locations
+ */
+ public function setLocations(?GraphQLErrorLocationCollection $locations): void
+ {
+ $this->locations = $locations;
+ }
+
+ /**
+ * @param ?array $path
+ */
+ public function setPath(?array $path): void
+ {
+ $this->path = $path;
+ }
+
+ /**
+ * @param ?GraphQLErrorObject $extensions
+ */
+ public function setExtensions(?GraphQLErrorObject $extensions): void
+ {
+ $this->extensions = $extensions;
+ }
+
+
+
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLRequest.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLRequest.php
new file mode 100644
index 00000000000..6566ecd3e28
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLRequest.php
@@ -0,0 +1,59 @@
+String representation of the Source Text of the Document that is specified in the Language section of the GraphQL specification.
+ *
+
+ * @return null|string
+ */
+ public function getQuery();
+
+ /**
+ * Name of the operation, if you defined several operations in query
.
+ *
+
+ * @return null|string
+ */
+ public function getOperationName();
+
+ /**
+ * JSON object that contains key-value pairs in which the keys are variable names and the values are variable values.
+ *
+
+ * @return null|GraphQLVariablesMap
+ */
+ public function getVariables();
+
+ /**
+ * @param ?string $query
+ */
+ public function setQuery(?string $query): void;
+
+ /**
+ * @param ?string $operationName
+ */
+ public function setOperationName(?string $operationName): void;
+
+ /**
+ * @param ?GraphQLVariablesMap $variables
+ */
+ public function setVariables(?GraphQLVariablesMap $variables): void;
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLRequestBuilder.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLRequestBuilder.php
new file mode 100644
index 00000000000..14faed70dc3
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLRequestBuilder.php
@@ -0,0 +1,131 @@
+
+ */
+final class GraphQLRequestBuilder implements Builder
+{
+ /**
+
+ * @var ?string
+ */
+ private $query;
+
+ /**
+
+ * @var ?string
+ */
+ private $operationName;
+
+ /**
+
+ * @var null|GraphQLVariablesMap|GraphQLVariablesMapBuilder
+ */
+ private $variables;
+
+ /**
+ * String representation of the Source Text of the Document that is specified in the Language section of the GraphQL specification.
+ *
+
+ * @return null|string
+ */
+ public function getQuery()
+ {
+ return $this->query;
+ }
+
+ /**
+ * Name of the operation, if you defined several operations in query
.
+ *
+
+ * @return null|string
+ */
+ public function getOperationName()
+ {
+ return $this->operationName;
+ }
+
+ /**
+ * JSON object that contains key-value pairs in which the keys are variable names and the values are variable values.
+ *
+
+ * @return null|GraphQLVariablesMap
+ */
+ public function getVariables()
+ {
+ return $this->variables instanceof GraphQLVariablesMapBuilder ? $this->variables->build() : $this->variables;
+ }
+
+ /**
+ * @param ?string $query
+ * @return $this
+ */
+ public function withQuery(?string $query)
+ {
+ $this->query = $query;
+
+ return $this;
+ }
+
+ /**
+ * @param ?string $operationName
+ * @return $this
+ */
+ public function withOperationName(?string $operationName)
+ {
+ $this->operationName = $operationName;
+
+ return $this;
+ }
+
+ /**
+ * @param ?GraphQLVariablesMap $variables
+ * @return $this
+ */
+ public function withVariables(?GraphQLVariablesMap $variables)
+ {
+ $this->variables = $variables;
+
+ return $this;
+ }
+
+ /**
+ * @deprecated use withVariables() instead
+ * @return $this
+ */
+ public function withVariablesBuilder(?GraphQLVariablesMapBuilder $variables)
+ {
+ $this->variables = $variables;
+
+ return $this;
+ }
+
+ public function build(): GraphQLRequest
+ {
+ return new GraphQLRequestModel(
+ $this->query,
+ $this->operationName,
+ $this->variables instanceof GraphQLVariablesMapBuilder ? $this->variables->build() : $this->variables
+ );
+ }
+
+ public static function of(): GraphQLRequestBuilder
+ {
+ return new self();
+ }
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLRequestCollection.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLRequestCollection.php
new file mode 100644
index 00000000000..cd040d5d296
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLRequestCollection.php
@@ -0,0 +1,56 @@
+
+ * @method GraphQLRequest current()
+ * @method GraphQLRequest end()
+ * @method GraphQLRequest at($offset)
+ */
+class GraphQLRequestCollection extends MapperSequence
+{
+ /**
+ * @psalm-assert GraphQLRequest $value
+ * @psalm-param GraphQLRequest|stdClass $value
+ * @throws InvalidArgumentException
+ *
+ * @return GraphQLRequestCollection
+ */
+ public function add($value)
+ {
+ if (!$value instanceof GraphQLRequest) {
+ throw new InvalidArgumentException();
+ }
+ $this->store($value);
+
+ return $this;
+ }
+
+ /**
+ * @psalm-return callable(int):?GraphQLRequest
+ */
+ protected function mapper()
+ {
+ return function (?int $index): ?GraphQLRequest {
+ $data = $this->get($index);
+ if ($data instanceof stdClass) {
+ /** @var GraphQLRequest $data */
+ $data = GraphQLRequestModel::of($data);
+ $this->set($data, $index);
+ }
+
+ return $data;
+ };
+ }
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLRequestModel.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLRequestModel.php
new file mode 100644
index 00000000000..be24deceaf3
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLRequestModel.php
@@ -0,0 +1,145 @@
+query = $query;
+ $this->operationName = $operationName;
+ $this->variables = $variables;
+
+ }
+
+ /**
+ * String representation of the Source Text of the Document that is specified in the Language section of the GraphQL specification.
+ *
+ *
+ * @return null|string
+ */
+ public function getQuery()
+ {
+ if (is_null($this->query)) {
+ /** @psalm-var ?string $data */
+ $data = $this->raw(self::FIELD_QUERY);
+ if (is_null($data)) {
+ return null;
+ }
+ $this->query = (string) $data;
+ }
+
+ return $this->query;
+ }
+
+ /**
+ * Name of the operation, if you defined several operations in query
.
+ *
+ *
+ * @return null|string
+ */
+ public function getOperationName()
+ {
+ if (is_null($this->operationName)) {
+ /** @psalm-var ?string $data */
+ $data = $this->raw(self::FIELD_OPERATION_NAME);
+ if (is_null($data)) {
+ return null;
+ }
+ $this->operationName = (string) $data;
+ }
+
+ return $this->operationName;
+ }
+
+ /**
+ * JSON object that contains key-value pairs in which the keys are variable names and the values are variable values.
+ *
+ *
+ * @return null|GraphQLVariablesMap
+ */
+ public function getVariables()
+ {
+ if (is_null($this->variables)) {
+ /** @psalm-var stdClass|array|null $data */
+ $data = $this->raw(self::FIELD_VARIABLES);
+ if (is_null($data)) {
+ return null;
+ }
+
+ $this->variables = GraphQLVariablesMapModel::of($data);
+ }
+
+ return $this->variables;
+ }
+
+
+ /**
+ * @param ?string $query
+ */
+ public function setQuery(?string $query): void
+ {
+ $this->query = $query;
+ }
+
+ /**
+ * @param ?string $operationName
+ */
+ public function setOperationName(?string $operationName): void
+ {
+ $this->operationName = $operationName;
+ }
+
+ /**
+ * @param ?GraphQLVariablesMap $variables
+ */
+ public function setVariables(?GraphQLVariablesMap $variables): void
+ {
+ $this->variables = $variables;
+ }
+
+
+
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLResponse.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLResponse.php
new file mode 100644
index 00000000000..266747aa1fa
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLResponse.php
@@ -0,0 +1,45 @@
+JSON object that contains the results of a GraphQL query.
+ *
+
+ * @return null|mixed
+ */
+ public function getData();
+
+ /**
+ * Errors that the GraphQL query returns.
+ *
+
+ * @return null|GraphQLErrorCollection
+ */
+ public function getErrors();
+
+ /**
+ * @param mixed $data
+ */
+ public function setData( $data): void;
+
+ /**
+ * @param ?GraphQLErrorCollection $errors
+ */
+ public function setErrors(?GraphQLErrorCollection $errors): void;
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLResponseBuilder.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLResponseBuilder.php
new file mode 100644
index 00000000000..0f880b062be
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLResponseBuilder.php
@@ -0,0 +1,92 @@
+
+ */
+final class GraphQLResponseBuilder implements Builder
+{
+ /**
+
+ * @var null|mixed|mixed
+ */
+ private $data;
+
+ /**
+
+ * @var ?GraphQLErrorCollection
+ */
+ private $errors;
+
+ /**
+ * JSON object that contains the results of a GraphQL query.
+ *
+
+ * @return null|mixed
+ */
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ /**
+ * Errors that the GraphQL query returns.
+ *
+
+ * @return null|GraphQLErrorCollection
+ */
+ public function getErrors()
+ {
+ return $this->errors;
+ }
+
+ /**
+ * @param mixed $data
+ * @return $this
+ */
+ public function withData( $data)
+ {
+ $this->data = $data;
+
+ return $this;
+ }
+
+ /**
+ * @param ?GraphQLErrorCollection $errors
+ * @return $this
+ */
+ public function withErrors(?GraphQLErrorCollection $errors)
+ {
+ $this->errors = $errors;
+
+ return $this;
+ }
+
+
+ public function build(): GraphQLResponse
+ {
+ return new GraphQLResponseModel(
+ $this->data,
+ $this->errors
+ );
+ }
+
+ public static function of(): GraphQLResponseBuilder
+ {
+ return new self();
+ }
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLResponseCollection.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLResponseCollection.php
new file mode 100644
index 00000000000..d89d3a414b1
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLResponseCollection.php
@@ -0,0 +1,56 @@
+
+ * @method GraphQLResponse current()
+ * @method GraphQLResponse end()
+ * @method GraphQLResponse at($offset)
+ */
+class GraphQLResponseCollection extends MapperSequence
+{
+ /**
+ * @psalm-assert GraphQLResponse $value
+ * @psalm-param GraphQLResponse|stdClass $value
+ * @throws InvalidArgumentException
+ *
+ * @return GraphQLResponseCollection
+ */
+ public function add($value)
+ {
+ if (!$value instanceof GraphQLResponse) {
+ throw new InvalidArgumentException();
+ }
+ $this->store($value);
+
+ return $this;
+ }
+
+ /**
+ * @psalm-return callable(int):?GraphQLResponse
+ */
+ protected function mapper()
+ {
+ return function (?int $index): ?GraphQLResponse {
+ $data = $this->get($index);
+ if ($data instanceof stdClass) {
+ /** @var GraphQLResponse $data */
+ $data = GraphQLResponseModel::of($data);
+ $this->set($data, $index);
+ }
+
+ return $data;
+ };
+ }
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLResponseModel.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLResponseModel.php
new file mode 100644
index 00000000000..7f1e4f8f1ed
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLResponseModel.php
@@ -0,0 +1,108 @@
+data = $data;
+ $this->errors = $errors;
+
+ }
+
+ /**
+ * JSON object that contains the results of a GraphQL query.
+ *
+ *
+ * @return null|mixed
+ */
+ public function getData()
+ {
+ if (is_null($this->data)) {
+ /** @psalm-var mixed $data */
+ $data = $this->raw(self::FIELD_DATA);
+ if (is_null($data)) {
+ return null;
+ }
+ $this->data = $data;
+ }
+
+ return $this->data;
+ }
+
+ /**
+ * Errors that the GraphQL query returns.
+ *
+ *
+ * @return null|GraphQLErrorCollection
+ */
+ public function getErrors()
+ {
+ if (is_null($this->errors)) {
+ /** @psalm-var ?list $data */
+ $data = $this->raw(self::FIELD_ERRORS);
+ if (is_null($data)) {
+ return null;
+ }
+ $this->errors = GraphQLErrorCollection::fromArray($data);
+ }
+
+ return $this->errors;
+ }
+
+
+ /**
+ * @param mixed $data
+ */
+ public function setData( $data): void
+ {
+ $this->data = $data;
+ }
+
+ /**
+ * @param ?GraphQLErrorCollection $errors
+ */
+ public function setErrors(?GraphQLErrorCollection $errors): void
+ {
+ $this->errors = $errors;
+ }
+
+
+
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLVariablesMap.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLVariablesMap.php
new file mode 100644
index 00000000000..6bf2f65ca70
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLVariablesMap.php
@@ -0,0 +1,20 @@
+
+ */
+final class GraphQLVariablesMapBuilder implements Builder
+{
+
+
+
+
+ public function build(): GraphQLVariablesMap
+ {
+ return new GraphQLVariablesMapModel(
+ );
+ }
+
+ public static function of(): GraphQLVariablesMapBuilder
+ {
+ return new self();
+ }
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLVariablesMapCollection.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLVariablesMapCollection.php
new file mode 100644
index 00000000000..4d4f1ffbc52
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLVariablesMapCollection.php
@@ -0,0 +1,56 @@
+
+ * @method GraphQLVariablesMap current()
+ * @method GraphQLVariablesMap end()
+ * @method GraphQLVariablesMap at($offset)
+ */
+class GraphQLVariablesMapCollection extends MapperSequence
+{
+ /**
+ * @psalm-assert GraphQLVariablesMap $value
+ * @psalm-param GraphQLVariablesMap|stdClass $value
+ * @throws InvalidArgumentException
+ *
+ * @return GraphQLVariablesMapCollection
+ */
+ public function add($value)
+ {
+ if (!$value instanceof GraphQLVariablesMap) {
+ throw new InvalidArgumentException();
+ }
+ $this->store($value);
+
+ return $this;
+ }
+
+ /**
+ * @psalm-return callable(int):?GraphQLVariablesMap
+ */
+ protected function mapper()
+ {
+ return function (?int $index): ?GraphQLVariablesMap {
+ $data = $this->get($index);
+ if ($data instanceof stdClass) {
+ /** @var GraphQLVariablesMap $data */
+ $data = GraphQLVariablesMapModel::of($data);
+ $this->set($data, $index);
+ }
+
+ return $data;
+ };
+ }
+}
diff --git a/lib/commercetools-history/src/Models/GraphQl/GraphQLVariablesMapModel.php b/lib/commercetools-history/src/Models/GraphQl/GraphQLVariablesMapModel.php
new file mode 100644
index 00000000000..5d51bedcfa5
--- /dev/null
+++ b/lib/commercetools-history/src/Models/GraphQl/GraphQLVariablesMapModel.php
@@ -0,0 +1,55 @@
+raw($key);
+ if (is_null($data)) {
+ return null;
+ }
+ if (preg_match(GraphQLVariablesMap::FIELD_PATTERN0, $key) === 1) {
+ /** @psalm-var stdClass $data */
+ return JsonObjectModel::of($data);
+ }
+
+ return $data;
+ }
+
+
+}
diff --git a/references.txt b/references.txt
index 0ed8c9710a0..754e1fdec88 100644
--- a/references.txt
+++ b/references.txt
@@ -344,3 +344,4 @@ e5666d7956fd6de53e41f965b052896d2a2ddd9b
98343f9a365a2f9d4db86190003d00b8682c71d9
e8f731a742ca2997342b1b5f98121d86c8189a3b
473d2c6f1a9e9da0d2a42408ccf11f27a838f8b5
+b7b82f5a701908239fcf39781bcbfa80823c9fa0