Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions config/graphql.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@
//
],

/*
|--------------------------------------------------------------------------
| Mutations
|--------------------------------------------------------------------------
|
| Here you may list mutations to be added to the Statamic schema.
|
| https://statamic.dev/graphql#custom-mutations
*/

'mutations' => [
//
],

/*
|--------------------------------------------------------------------------
| Middleware
Expand Down
7 changes: 6 additions & 1 deletion src/GraphQL/DefaultSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function getConfig()
{
return [
'query' => $this->getQueries(),
'mutation' => [],
'mutation' => $this->getMutations(),
'middleware' => $this->getMiddleware(),
'method' => ['GET', 'POST'],
];
Expand Down Expand Up @@ -82,4 +82,9 @@ private function getMiddleware()
GraphQL::getExtraMiddleware()
);
}

private function getMutations()
{
return config('statamic.graphql.mutations', []);
}
}
11 changes: 11 additions & 0 deletions src/GraphQL/Middleware/CacheResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ public function handle($request, Closure $next)
return $next($request);
}

if ($this->isMutation($request)) {
return $next($request);
}

$cache = app(ResponseCache::class);

if ($response = $cache->get($request)) {
Expand All @@ -25,4 +29,11 @@ public function handle($request, Closure $next)

return $response;
}

protected function isMutation($request): bool
{
$query = ltrim(strtolower($request->get('query', '')));

return str_starts_with($query, 'mutation');
}
}
142 changes: 142 additions & 0 deletions tests/Feature/GraphQL/CustomMutationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?php

namespace Tests\Feature\GraphQL;

use GraphQL\Type\Definition\Type;
use Orchestra\Testbench\Attributes\DefineEnvironment;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Test;
use Rebing\GraphQL\Support\Mutation;
use Statamic\Facades\GraphQL;
use Tests\TestCase;

#[Group('graphql')]
class CustomMutationTest extends TestCase
{
public function setUp(): void
{
parent::setUp();

app()->instance('mutation-count', 0);
}

#[Test]
public function custom_mutation_does_not_yet_exist()
{
$this
->post('/graphql', ['query' => 'mutation { createItem(name: "test") }'])
->assertJson(['errors' => [[
'message' => 'Schema is not configured for mutations.',
]]]);
}

#[Test]
#[DefineEnvironment('addCustomMutationsThroughConfig')]
public function a_custom_mutation_can_be_added_to_the_default_schema_through_config()
{
$this
->post('/graphql', ['query' => 'mutation { createItem(name: "test") }'])
->assertGqlOk()
->assertExactJson(['data' => ['createItem' => 'Item created: test']]);
}

#[Test]
#[DefineEnvironment('addCustomMutationsThroughConfig')]
public function multiple_custom_mutations_can_be_added()
{
$this
->post('/graphql', ['query' => 'mutation { createItem(name: "first") }'])
->assertGqlOk()
->assertExactJson(['data' => ['createItem' => 'Item created: first']]);

$this
->post('/graphql', ['query' => 'mutation { updateItem(id: 1, name: "updated") }'])
->assertGqlOk()
->assertExactJson(['data' => ['updateItem' => 'Item 1 updated: updated']]);
}

#[Test]
#[DefineEnvironment('addCustomMutationsThroughConfig')]
public function mutations_are_not_cached()
{
$this
->post('/graphql', ['query' => 'mutation { createItem(name: "test") }'])
->assertGqlOk()
->assertExactJson(['data' => ['createItem' => 'Item created: test']]);

$this
->post('/graphql', ['query' => 'mutation { createItem(name: "test") }'])
->assertGqlOk()
->assertExactJson(['data' => ['createItem' => 'Item created: test']]);

$this->assertEquals(2, app('mutation-count'));
}

protected function addCustomMutationsThroughConfig($app)
{
$app['config']->set('statamic.graphql.mutations', [
CreateItemMutation::class,
UpdateItemMutation::class,
]);
}
}

class CreateItemMutation extends Mutation
{
protected $attributes = [
'name' => 'createItem',
];

public function type(): Type
{
return GraphQL::string();
}

public function args(): array
{
return [
'name' => [
'type' => Type::nonNull(Type::string()),
'description' => 'The name of the item to create',
],
];
}

public function resolve($root, $args)
{
app()->instance('mutation-count', app('mutation-count') + 1);

return 'Item created: '.$args['name'];
}
}

class UpdateItemMutation extends Mutation
{
protected $attributes = [
'name' => 'updateItem',
];

public function type(): Type
{
return GraphQL::string();
}

public function args(): array
{
return [
'id' => [
'type' => Type::nonNull(Type::int()),
'description' => 'The ID of the item to update',
],
'name' => [
'type' => Type::nonNull(Type::string()),
'description' => 'The new name of the item',
],
];
}

public function resolve($root, $args)
{
return "Item {$args['id']} updated: {$args['name']}";
}
}