-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
API: Introduce FormMutation and FormSubmission (#17)
- Loading branch information
Showing
8 changed files
with
383 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
SilverStripe\GraphQL: | ||
schema: | ||
types: | ||
fieldSubmission: 'SilverStripe\GraphQL\Forms\FieldSubmissionTypeCreator' | ||
formResult: 'SilverStripe\GraphQL\Forms\FormResultTypeCreator' | ||
mutations: | ||
submitForm: 'SilverStripe\GraphQL\Forms\FormMutationCreator' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<?php | ||
|
||
namespace SilverStripe\GraphQL; | ||
|
||
use SilverStripe\Core\Object; | ||
use GraphQL\Type\Definition\ScalarType; | ||
use SilverStripe\GraphQL\Manager; | ||
|
||
/** | ||
* A custom {@link ScalarType} to represent arbitrary JSON based back to the | ||
* client. | ||
*/ | ||
|
||
class JsonType extends ScalarType { | ||
|
||
/** | ||
* @var string $name | ||
*/ | ||
public $name = 'JsonType'; | ||
|
||
|
||
public function __construct() { | ||
parent::__construct(); | ||
} | ||
|
||
/** | ||
* @param array $value | ||
* | ||
* @return string | ||
*/ | ||
public function serialize($value) { | ||
return json_encode($value); | ||
} | ||
|
||
/** | ||
* @param array $value | ||
* | ||
* @return string | ||
*/ | ||
public function parseValue($value) { | ||
return $value; | ||
} | ||
|
||
/** | ||
* @param GraphQL\Language\AST\Value | ||
*/ | ||
public function parseLiteral($valueAST) { | ||
return $valueAST; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<?php | ||
|
||
namespace SilverStripe\GraphQL\Forms; | ||
|
||
use GraphQL\Type\Definition\Type; | ||
use SilverStripe\GraphQL\TypeCreator; | ||
|
||
/** | ||
* Represents a single field submission with a name and value within a form. | ||
* | ||
* Form values are not strictly typed. They will be resolved as strings. | ||
* | ||
* @todo strictly type input? | ||
* @todo how would Form array submit? | ||
*/ | ||
class FieldSubmissionTypeCreator extends TypeCreator | ||
{ | ||
/** | ||
* Use on input of the form. GraphQL will not return a fieldSubmission but | ||
* it will use it for the input. | ||
* | ||
* @var boolean | ||
*/ | ||
protected $inputObject = true; | ||
|
||
/** | ||
* @return array | ||
*/ | ||
public function attributes() | ||
{ | ||
return [ | ||
'name' => 'fieldSubmission', | ||
]; | ||
} | ||
|
||
/** | ||
* @return array | ||
*/ | ||
public function fields() | ||
{ | ||
return [ | ||
'name' => [ | ||
'type' => Type::nonNull(Type::string()), | ||
'description' => 'Form field name' | ||
], | ||
'value' => [ | ||
'type' => Type::string(), | ||
'description' => 'Form field value' | ||
], | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
<?php | ||
|
||
namespace SilverStripe\GraphQL\Forms; | ||
|
||
use SilverStripe\GraphQL\MutationCreator; | ||
use SilverStripe\Core\Injector\Injector; | ||
use GraphQL\Type\Definition\Type; | ||
|
||
// temp work around | ||
use SilverStripe\AssetAdmin\Controller\AssetAdmin; | ||
|
||
/** | ||
* This GraphQL mutation handles submissions from a form with a given URL and | ||
* delegates the handling of the form to the specific {@link Form}. | ||
* | ||
* A GraphQL submission outline looks like the following: | ||
* | ||
* <code> | ||
* mutation ($data:[fieldSubmission]!, $className:String!, $action:String!) { | ||
* submitForm(data:$data, className:$className, action: $action) { | ||
* errors | ||
* } | ||
* } | ||
* | ||
* { | ||
* "data": [{name: "Field", value}], | ||
* "className": "SilverStripe\\Form", | ||
* "action": "action_submit" | ||
* } | ||
* </code> | ||
*/ | ||
class FormMutationCreator extends MutationCreator { | ||
|
||
/** | ||
* @return array | ||
*/ | ||
public function attributes() | ||
{ | ||
return [ | ||
'name' => 'submitForm' | ||
]; | ||
} | ||
|
||
/** | ||
* The result of a standard mutation via a form is a GraphQL `formResult`. | ||
* | ||
* See {@link FormResultTypeCreator} | ||
* | ||
* @return callable | ||
*/ | ||
public function type() | ||
{ | ||
return function() { | ||
return $this->manager->getType('formResult'); | ||
}; | ||
} | ||
|
||
/** | ||
* Form submission. | ||
* | ||
* The fully qualified className of the {@link FormFactory} should be passed | ||
* along with the mutation to recreate the form object. Any context required | ||
* for the form to recreate itself should be passed as `data` similar to a | ||
* standard POST request. | ||
* | ||
* @return array | ||
*/ | ||
public function args() | ||
{ | ||
return [ | ||
'data' => [ | ||
'type' => Type::listOf($this->manager->getType('fieldSubmission')), | ||
'description' => 'List of fields submitted' | ||
], | ||
'className' => [ | ||
'type' => Type::nonNull(Type::string()), | ||
'description' => 'Fully qualified class name for the given form instance' | ||
], | ||
'action' => [ | ||
'type' => Type::nonNull(Type::string()), | ||
'description' => 'Name of the FormAction to trigger' | ||
], | ||
]; | ||
} | ||
|
||
/** | ||
* Handle the form submission. | ||
* | ||
* Initiates a new instance of the {@link Form} record and hands it's off | ||
* to its' resolve function. Note that this process will bypass the | ||
* controller context that the form is operating on so the form has to be | ||
* responsible for permission checking. All context required for the form | ||
* should be passed either as POST data attached to the form or as part of | ||
* the scheme. | ||
* | ||
* Returns a GraphQL `FormResultTypeCreator` type containing any updated | ||
* record keys and the new {@link FormSchema} as the form may change | ||
* behavior with updated data (i.e when saving a page, new fields may be | ||
* added) | ||
* | ||
* @param Object $object | ||
* @param array $args | ||
* @param array $context | ||
* @param array $info | ||
* | ||
* @todo requires silverstripe-framework/issues/6334 | ||
* | ||
* @return array | ||
*/ | ||
public function resolve($object, array $args, $context, $info) | ||
{ | ||
// move the return to the specific form cases | ||
$form = AssetAdmin::create()->getFileEditForm(3); | ||
$schema = Injector::inst()->create('FormSchema'); | ||
|
||
// requires silverstripe-framework/issues/6334 | ||
return [ | ||
'messages' => null, | ||
'schema' => $schema->getSchema($form), | ||
'state' => $schema->getState($form), | ||
'errors' => null, | ||
'updatedRecords' => [ | ||
'File:3' | ||
] | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
<?php | ||
|
||
namespace SilverStripe\GraphQL\Forms; | ||
|
||
use GraphQL\Type\Definition\Type; | ||
use SilverStripe\GraphQL\TypeCreator; | ||
use SilverStripe\GraphQL\JsonType; | ||
|
||
/** | ||
* Encapsulates a GraphQL response to a {@link FormSubmissionTypeCreator}. | ||
* | ||
*/ | ||
class FormResultTypeCreator extends TypeCreator | ||
{ | ||
/** | ||
* @return array | ||
*/ | ||
public function attributes() | ||
{ | ||
return [ | ||
'name' => 'formResult' | ||
]; | ||
} | ||
|
||
/** | ||
* @return array | ||
*/ | ||
public function fields() | ||
{ | ||
$json = new JsonType(); | ||
|
||
return [ | ||
'updateQueries' => [ | ||
'type' => Type::listOf(Type::string()), | ||
'description' => 'A list of named queries to re-evaluate' | ||
], | ||
'updatedRecords' => [ | ||
'type' => Type::listOf(Type::string()), | ||
'description' => 'A list of modified keys for Apollo to fetch.' | ||
], | ||
'addedRecords' => [ | ||
'type' => Type::listOf(Type::string()), | ||
'description' => 'A list of added record keys' | ||
], | ||
'deletedRecords' => [ | ||
'type' => Type::listOf(Type::string()), | ||
'description' => 'A list of deleted record keys' | ||
], | ||
'schema' => [ | ||
'type' => $json, | ||
'description' => 'Updated form schema' | ||
], | ||
'state' => [ | ||
'type' => $json, | ||
'description' => 'Updated form state' | ||
], | ||
'errors' => [ | ||
'type' => Type::listOf(Type::string()), | ||
'description' => 'Error messages' | ||
], | ||
'messages' => [ | ||
'type' => Type::listOf(Type::string()), | ||
'description' => 'Success message' | ||
], | ||
]; | ||
} | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php | ||
|
||
namespace SilverStripe\GraphQL\Tests; | ||
|
||
use SilverStripe\Dev\TestOnly; | ||
use SilverStripe\ORM\DataObject; | ||
|
||
class FormFake extends Form implements TestOnly | ||
{ | ||
public function Fields() | ||
{ | ||
return new FieldList( | ||
new TextField('Name'), | ||
new EmailField('Email') | ||
); | ||
} | ||
|
||
public function getAllActions() | ||
{ | ||
return new FieldList( | ||
FormAction::create('submit', 'Submit')->submit(function() { | ||
// @todo, requires a core framework change | ||
}) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?php | ||
|
||
namespace SilverStripe\Tests\GraphQL; | ||
|
||
use SilverStripe\Control\HTTPRequest; | ||
use SilverStripe\Dev\SapphireTest; | ||
use SilverStripe\GraphQL\Manager; | ||
use SilverStripe\GraphQL\Controller; | ||
use SilverStripe\GraphQL\Tests\Fake\FormFake; | ||
use SilverStripe\Core\Config\Config; | ||
use GraphQL\Schema; | ||
use GraphQL\Type\Definition\ObjectType; | ||
use ReflectionClass; | ||
use Exception; | ||
|
||
class FormTest extends SapphireTest | ||
{ | ||
public function testFormSubmission() | ||
{ | ||
$controller = new Controller(); | ||
$manager = new Manager(); | ||
|
||
$controller->setManager($manager); | ||
$response = $controller->index(new HTTPRequest('POST', '', '', [ | ||
'operationName' => 'SubmitForm', | ||
'query' => ' | ||
mutation SubmitForm($data: [fieldSubmission]!, $className: String!, $action: String!) { | ||
submitForm(data: $data, className: $className, action: $action) { | ||
messages | ||
updateQueries | ||
updatedRecords | ||
addedRecords | ||
deletedRecords | ||
scheme | ||
errors | ||
messages | ||
} | ||
}', | ||
'variables' => [ | ||
'action' => 'submit', | ||
'className' => 'SilverStripe\\GraphQL\\Tests\\Fake\\FormFake', | ||
'data' => [ | ||
'name' => 'Name', | ||
'email' => '[email protected]' | ||
] | ||
] | ||
])); | ||
|
||
$this->assertFalse($response->isError()); | ||
} | ||
} |