Skip to content

Commit

Permalink
Add GraphQL support for querying submissions
Browse files Browse the repository at this point in the history
  • Loading branch information
engram-design committed Nov 4, 2022
1 parent da1d835 commit 5dad4f8
Show file tree
Hide file tree
Showing 11 changed files with 378 additions and 1 deletion.
3 changes: 2 additions & 1 deletion docs/.sidebar.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"children": [
"developers/submission",
"developers/review",
"developers/events"
"developers/events",
"developers/graphql"
]
}
]
84 changes: 84 additions & 0 deletions docs/developers/graphql.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# GraphQL
Workflow supports accessing [Submission](docs:developers/submission) objects via GraphQL. Be sure to read about [Craft's GraphQL support](https://craftcms.com/docs/4.x/graphql.html).

## Submissions

### Example

:::code
```graphql GraphQL
{
workflowSubmissions (status: "pending") {
id
status

owner {
title
}
}
}
```

```json JSON Response
{
"data": {
"workflowSubmissions": [
{
"id": "5673",
"title": "pending",
"owner": {
"title": "My First Blog Post"
}
}
]
}
}
```
:::

### The `workflowSubmissions` query
This query is used to query for submissions.

| Argument | Type | Description
| - | - | -
| `id`| `[QueryArgument]` | Narrows the query results based on the elements’ IDs.
| `uid`| `[String]` | Narrows the query results based on the elements’ UIDs.
| `status`| `[String]` | Narrows the query results based on the elements’ statuses.
| `archived`| `Boolean` | Narrows the query results to only elements that have been archived.
| `trashed`| `Boolean` | Narrows the query results to only elements that have been soft-deleted.
| `site`| `[String]` | Determines which site(s) the elements should be queried in. Defaults to the current (requested) site.
| `siteId`| `String` | Determines which site(s) the elements should be queried in. Defaults to the current (requested) site.
| `unique`| `Boolean` | Determines whether only elements with unique IDs should be returned by the query.
| `enabledForSite`| `Boolean` | Narrows the query results based on whether the elements are enabled in the site they’re being queried in, per the `site` argument.
| `title`| `[String]` | Narrows the query results based on the elements’ titles.
| `search`| `String` | Narrows the query results to only elements that match a search query.
| `relatedTo`| `[Int]` | Narrows the query results to elements that relate to *any* of the provided element IDs. This argument is ignored, if `relatedToAll` is also used.
| `relatedToAll`| `[Int]` | Narrows the query results to elements that relate to *all* the provided element IDs. Using this argument will cause `relatedTo` argument to be ignored.
| `ref`| `[String]` | Narrows the query results based on a reference string.
| `fixedOrder`| `Boolean` | Causes the query results to be returned in the order specified by the `id` argument.
| `inReverse`| `Boolean` | Causes the query results to be returned in reverse order.
| `dateCreated`| `[String]` | Narrows the query results based on the elements’ creation dates.
| `dateUpdated`| `[String]` | Narrows the query results based on the elements’ last-updated dates.
| `offset`| `Int` | Sets the offset for paginated results.
| `limit`| `Int` | Sets the limit for paginated results.
| `orderBy`| `String` | Sets the field the returned elements should be ordered by
| `ownerId`| `[QueryArgument]` | Narrows the query results based on the owner element the submission was made on, per the owners’ IDs.

### The `SubmissionInterface` interface
This is the interface implemented by all submissions.

| Field | Type | Description
| - | - | -
| `id`| `ID` | The id of the entity
| `uid`| `String` | The uid of the entity
| `_count`| `Int` | Return a number of related elements for a field.
| `enabled`| `Boolean` | Whether the element is enabled or not.
| `archived`| `Boolean` | Whether the element is archived or not.
| `siteId`| `Int` | The ID of the site the element is associated with.
| `searchScore`| `String` | The element’s search score, if the `search` parameter was used when querying for the element.
| `trashed`| `Boolean` | Whether the element has been soft-deleted or not.
| `status`| `String` | The element's status.
| `dateCreated`| `DateTime` | The date the element was created.
| `dateUpdated`| `DateTime` | The date the element was last updated.
| `ownerId`| `Int` | The ID of the element that the submission relates to.
| `owner`| `ElementInterface` | The element that the submission relates to.
28 changes: 28 additions & 0 deletions src/Workflow.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

use verbb\workflow\base\PluginTrait;
use verbb\workflow\elements\Submission;
use verbb\workflow\gql\interfaces\SubmissionInterface;
use verbb\workflow\gql\queries\SubmissionQuery;
use verbb\workflow\models\Settings;
use verbb\workflow\variables\WorkflowVariable;
use verbb\workflow\widgets\Submissions as SubmissionsWidget;
Expand All @@ -17,12 +19,16 @@
use craft\events\DefineConsoleActionsEvent;
use craft\events\RegisterComponentTypesEvent;
use craft\events\RegisterEmailMessagesEvent;
use craft\events\RegisterGqlQueriesEvent;
use craft\events\RegisterGqlSchemaComponentsEvent;
use craft\events\RegisterGqlTypesEvent;
use craft\events\RegisterUrlRulesEvent;
use craft\events\RegisterUserPermissionsEvent;
use craft\helpers\UrlHelper;
use craft\services\Dashboard;
use craft\services\Drafts;
use craft\services\Elements;
use craft\services\Gql;
use craft\services\SystemMessages;
use craft\services\UserPermissions;
use craft\web\UrlManager;
Expand Down Expand Up @@ -65,6 +71,7 @@ public function init(): void
$this->_registerVariables();
$this->_registerCraftEventListeners();
$this->_registerElementTypes();
$this->_registerGraphQl();

if (Craft::$app->getRequest()->getIsCpRequest()) {
$this->_registerCpRoutes();
Expand Down Expand Up @@ -198,4 +205,25 @@ private function _registerResaveCommand(): void
];
});
}

private function _registerGraphQl(): void
{
Event::on(Gql::class, Gql::EVENT_REGISTER_GQL_TYPES, function(RegisterGqlTypesEvent $event) {
$event->types[] = SubmissionInterface::class;
});

Event::on(Gql::class, Gql::EVENT_REGISTER_GQL_QUERIES, function(RegisterGqlQueriesEvent $event) {
foreach (SubmissionQuery::getQueries() as $key => $value) {
$event->queries[$key] = $value;
}
});

Event::on(Gql::class, Gql::EVENT_REGISTER_GQL_SCHEMA_COMPONENTS, function(RegisterGqlSchemaComponentsEvent $event) {
$label = Craft::t('workflow', 'Workflow');

$event->queries[$label] = [
'workflowSubmissions:read' => ['label' => Craft::t('workflow', 'View submissions')],
];
});
}
}
5 changes: 5 additions & 0 deletions src/elements/Submission.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ protected static function defineSearchableAttributes(): array
return ['ownerTitle', 'editorName', 'publisherName'];
}

public static function gqlTypeNameByContext(mixed $context): string
{
return 'Submission';
}


// Properties
// =========================================================================
Expand Down
48 changes: 48 additions & 0 deletions src/gql/arguments/SubmissionArguments.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php
namespace verbb\workflow\gql\arguments;

use verbb\workflow\elements\Submission;

use Craft;
use craft\base\GqlInlineFragmentFieldInterface;
use craft\gql\base\ElementArguments;
use craft\gql\types\QueryArgument;

use GraphQL\Type\Definition\Type;

class SubmissionArguments extends ElementArguments
{
// Static Methods
// =========================================================================

public static function getArguments(): array
{
return array_merge(parent::getArguments(), self::getContentArguments(), [
'ownerId' => [
'name' => 'ownerId',
'type' => Type::listOf(QueryArgument::getType()),
'description' => 'Narrows the query results based on the owner element the submission was made on, per the owners’ IDs.',
],
]);
}

public static function getContentArguments(): array
{
$contentArguments = [];

$contentFields = Craft::$app->getFields()->getLayoutByType(Submission::class)->getCustomFields();

foreach ($contentFields as $contentField) {
if (!$contentField instanceof GqlInlineFragmentFieldInterface) {
$contentArguments[$contentField->handle] = $contentField->getContentGqlQueryArgumentType();
}
}

return array_merge(parent::getContentArguments(), $contentArguments);
}

public static function getRevisionArguments(): array
{
return [];
}
}
61 changes: 61 additions & 0 deletions src/gql/interfaces/SubmissionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php
namespace verbb\workflow\gql\interfaces;

use verbb\workflow\gql\types\generators\SubmissionGenerator;

use Craft;
use craft\gql\interfaces\Element;
use craft\gql\GqlEntityRegistry;

use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\Type;

class SubmissionInterface extends Element
{
// Static Methods
// =========================================================================

public static function getTypeGenerator(): string
{
return SubmissionGenerator::class;
}

public static function getType(): Type
{
if ($type = GqlEntityRegistry::getEntity(self::getName())) {
return $type;
}

$type = GqlEntityRegistry::createEntity(self::getName(), new InterfaceType([
'name' => static::getName(),
'fields' => self::class . '::getFieldDefinitions',
'description' => 'This is the interface implemented by all submissions.',
'resolveType' => self::class . '::resolveElementTypeName',
]));

SubmissionGenerator::generateTypes();

return $type;
}

public static function getName(): string
{
return 'SubmissionInterface';
}

public static function getFieldDefinitions(): array
{
return Craft::$app->getGql()->prepareFieldDefinitions(array_merge(parent::getFieldDefinitions(), [
'ownerId' => [
'name' => 'ownerId',
'type' => Type::int(),
'description' => 'The ID of the element that the submission relates to.',
],
'owner' => [
'name' => 'owner',
'type' => Element::getType(),
'description' => 'The element that the submission relates to.',
],
]), self::getName());
}
}
33 changes: 33 additions & 0 deletions src/gql/queries/SubmissionQuery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
namespace verbb\workflow\gql\queries;

use verbb\workflow\gql\arguments\SubmissionArguments;
use verbb\workflow\gql\interfaces\SubmissionInterface;
use verbb\workflow\gql\resolvers\SubmissionResolver;
use verbb\workflow\helpers\Gql as GqlHelper;

use craft\gql\base\Query;

use GraphQL\Type\Definition\Type;

class SubmissionQuery extends Query
{
// Static Methods
// =========================================================================

public static function getQueries(bool $checkToken = true): array
{
if ($checkToken && !GqlHelper::canQuerySubmissions()) {
return [];
}

return [
'workflowSubmissions' => [
'type' => Type::listOf(SubmissionInterface::getType()),
'args' => SubmissionArguments::getArguments(),
'resolve' => SubmissionResolver::class . '::resolve',
'description' => 'This query is used to query for workflow submissions.',
],
];
}
}
38 changes: 38 additions & 0 deletions src/gql/resolvers/SubmissionResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php
namespace verbb\workflow\gql\resolvers;

use verbb\workflow\elements\Submission;
use verbb\workflow\helpers\Gql as GqlHelper;

use craft\elements\db\ElementQuery;
use craft\gql\base\ElementResolver;
use craft\helpers\Db;

class SubmissionResolver extends ElementResolver
{
// Static Methods
// =========================================================================

public static function prepareQuery(mixed $source, array $arguments, ?string $fieldName = null): mixed
{
if ($source === null) {
$query = Submission::find();
} else {
$query = $source->$fieldName;
}

if (!$query instanceof ElementQuery) {
return $query;
}

foreach ($arguments as $key => $value) {
$query->$key($value);
}

if (!GqlHelper::canQuerySubmissions()) {
return [];
}

return $query;
}
}
21 changes: 21 additions & 0 deletions src/gql/types/SubmissionType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php
namespace verbb\workflow\gql\types;

use verbb\workflow\gql\interfaces\SubmissionInterface;

use craft\gql\types\elements\Element;

class SubmissionType extends Element
{
// Public Methods
// =========================================================================

public function __construct(array $config)
{
$config['interfaces'] = [
SubmissionInterface::getType(),
];

parent::__construct($config);
}
}
Loading

0 comments on commit 5dad4f8

Please sign in to comment.