Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NativeEnumTypeConfig to generate native PHP enums #92

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from
Draft
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
1 change: 0 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
/.gitattributes export-ignore
/.gitignore export-ignore
/.php-cs-fixer.php export-ignore
/infection.json export-ignore
/Makefile export-ignore
/phpstan.neon export-ignore
/phpunit.xml export-ignore
Expand Down
57 changes: 29 additions & 28 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
with:
coverage: none
extensions: mbstring
php-version: 8.1
php-version: 8.2

- name: "Validate composer.json and composer.lock"
run: composer validate
Expand All @@ -33,6 +33,20 @@ jobs:

runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
php-version:
- 7.4
- 8.0
- 8.1
- 8.2

dependencies:
- lowest
- locked
- highest

steps:
- name: "Checkout"
uses: actions/checkout@v2
Expand All @@ -42,13 +56,22 @@ jobs:
with:
coverage: none
extensions: mbstring
php-version: 8.1
php-version: ${{ matrix.php-version }}

- name: "Install lowest dependencies with composer"
if: matrix.dependencies == 'lowest'
run: composer update --prefer-lowest --no-interaction --no-progress

- name: "Install locked dependencies with composer"
if: matrix.dependencies == 'locked'
run: composer install --no-interaction --no-progress

- name: "Install highest dependencies with composer"
if: matrix.dependencies == 'highest'
run: composer update --no-interaction --no-progress

- name: "Run phpstan/phpstan"
run: vendor/bin/phpstan analyse --configuration=phpstan.neon
run: vendor/bin/phpstan analyse

tests:
name: "Tests"
Expand All @@ -62,7 +85,7 @@ jobs:
- 7.4
- 8.0
- 8.1

- 8.2
dependencies:
- lowest
- locked
Expand Down Expand Up @@ -119,7 +142,7 @@ jobs:
with:
coverage: none
extensions: mbstring
php-version: 8.1
php-version: 8.2
env:
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Expand All @@ -140,7 +163,7 @@ jobs:
with:
coverage: pcov
extensions: mbstring
php-version: 8.1
php-version: 8.2

- name: "Install locked dependencies with composer"
run: composer install --no-interaction --no-progress
Expand All @@ -150,25 +173,3 @@ jobs:

- name: "Send code coverage report to codecov.io"
uses: codecov/codecov-action@v2

mutation-tests:
name: "Mutation Tests"

runs-on: ubuntu-latest

steps:
- name: "Checkout"
uses: actions/checkout@v2

- name: "Setup PHP"
uses: shivammathur/setup-php@v2
with:
coverage: xdebug
extensions: mbstring
php-version: 8.1

- name: "Install locked dependencies with composer"
run: composer install --no-interaction --no-progress

- name: "Run mutation tests with infection/infection"
run: vendor/bin/infection
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Added

- Add `NativeEnumTypeConfig` to generate native PHP enums https://github.com/spawnia/sailor/pull/92

## v0.29.0

### Added
Expand Down
13 changes: 2 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,15 @@ fix: vendor

.PHONY: stan
stan: ## Runs static analysis with phpstan
mkdir -p .build/phpstan
vendor/bin/phpstan analyse --configuration=phpstan.neon
vendor/bin/phpstan analyse

.PHONY: test
test: ## Runs tests with phpunit
mkdir -p .build/phpunit
vendor/bin/phpunit

.PHONY: coverage
coverage: ## Collects coverage from running unit tests with phpunit
mkdir -p .build/phpunit
vendor/bin/phpunit --dump-xdebug-filter=.build/phpunit/xdebug-filter.php
vendor/bin/phpunit --coverage-text --prepend=.build/phpunit/xdebug-filter.php

.PHONY: infection
infection: ## Runs mutation tests with infection
mkdir -p .build/infection
vendor/bin/infection --ignore-msi-with-no-mutations --min-covered-msi=100 --min-msi=100
vendor/bin/phpunit --coverage-text

.PHONY: approve
approve: ## Generate code and approve it as expected
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ HelloSailor::setClient(null);
Custom scalars are commonly serialized as strings. Without knowing about the contents of the type,
Sailor can not do any conversions or provide more accurate type hints, so it uses `string`.

Enums are only supported from PHP 8.1. Many projects simply used scalar values or an implementation
that approximates enums through some kind of value class. Sailor is not opinionated and generates
enums as a class with string constants and does no conversion - useful but not perfect.
For an improved experience, it is recommended to customize the enum generation/conversion.
Since enums are only supported from PHP 8.1 and this library still supports PHP 7.4,
it generates enums as a class with string constants and handles values as `string`.
You may leverage native PHP enums by overriding `EndpointConfig::enumTypeConfig()`
and return an instance of `Spawnia\Sailor\Type\NativeEnumTypeConfig`.

Overwrite `EndpointConfig::configureTypes()` to specialize how Sailor deals with the types within your schema.
See [examples/custom-types](examples/custom-types).
Expand Down
26 changes: 8 additions & 18 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"require": {
"php": "^7.4 || ^8",
"ext-json": "*",
"nette/php-generator": "^3.6.7 || ^4",
"nette/php-generator": "^3.6.7 || ^4.0.8",
"psr/http-client": "^1",
"symfony/console": "^5 || ^6",
"symfony/var-exporter": "^5.3 || ^6",
Expand All @@ -29,21 +29,21 @@
"composer/composer": "^2",
"ergebnis/composer-normalize": "^2.13",
"guzzlehttp/guzzle": "^7",
"infection/infection": "~0.21",
"jangregor/phpstan-prophecy": "^1",
"mll-lab/php-cs-fixer-config": "^4.3",
"mockery/mockery": "^1.4",
"nyholm/psr7": "^1.4",
"ocramius/package-versions": "^1 || ^2",
"php-http/httplug": "^2",
"php-http/mock-client": "^1.4",
"phpstan/extension-installer": "^1",
"phpstan/phpstan": "^1",
"phpstan/phpstan": "^1.10.4",
"phpstan/phpstan-deprecation-rules": "^1",
"phpstan/phpstan-mockery": "^1",
"phpstan/phpstan-phpunit": "^1",
"phpstan/phpstan-strict-rules": "^1",
"phpunit/phpunit": "^9.5.2",
"spawnia/phpunit-assert-directory": "dev-php8 as 3.0.0",
"spawnia/phpunit-assert-directory": "^2.1",
"symfony/var-dumper": "^5.2.3",
"thecodingmachine/phpstan-safe-rule": "^1.1"
},
Expand All @@ -52,26 +52,20 @@
"guzzlehttp/guzzle": "Enables using the built-in default Client",
"mockery/mockery": "Used in Operation::mock()"
},
"repositories": [
{
"type": "git",
"url": "https://github.com/simpod/phpunit-assert-directory"
}
],
"autoload": {
"psr-4": {
"Spawnia\\Sailor\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Spawnia\\Sailor\\Tests\\": "tests/",
"Spawnia\\Sailor\\CustomTypes\\": "examples/custom-types/expected/",
"Spawnia\\Sailor\\CustomTypesSrc\\": "examples/custom-types/src/",
"Spawnia\\Sailor\\CustomTypes\\": "examples/custom-types/expected/",
"Spawnia\\Sailor\\Input\\": "examples/input/expected/",
"Spawnia\\Sailor\\Simple\\": "examples/simple/expected/",
"Spawnia\\Sailor\\PhpKeywords\\": "examples/php-keywords/expected/",
"Spawnia\\Sailor\\Polymorphic\\": "examples/polymorphic/expected/"
"Spawnia\\Sailor\\Polymorphic\\": "examples/polymorphic/expected/",
"Spawnia\\Sailor\\Simple\\": "examples/simple/expected/",
"Spawnia\\Sailor\\Tests\\": "tests/"
},
"files": [
"vendor/symfony/var-dumper/Resources/functions/dump.php"
Expand All @@ -85,12 +79,8 @@
"ergebnis/composer-normalize": true,
"ocramius/package-versions": true,
"phpstan/extension-installer": true,
"infection/extension-installer": true,
"php-http/discovery": false
},
"platform": {
"php": "7.4.15"
},
"preferred-install": "dist",
"sort-packages": true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ class MyBenSampoEnumQuery extends \Spawnia\Sailor\Operation
/**
* @param \Spawnia\Sailor\CustomTypes\Types\BenSampoEnum|null $value
*/
public static function execute($value = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.'): MyBenSampoEnumQuery\MyBenSampoEnumQueryResult
public static function execute(
$value = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.',
): MyBenSampoEnumQuery\MyBenSampoEnumQueryResult
{
return self::executeOperation(
$value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ class MyBenSampoEnumQuery extends \Spawnia\Sailor\ObjectLike
/**
* @param \Spawnia\Sailor\CustomTypes\Types\BenSampoEnum|null $withBenSampoEnum
*/
public static function make($withBenSampoEnum = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.'): self
public static function make(
$withBenSampoEnum = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.',
): self
{
$instance = new self;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ class MyCustomEnumQuery extends \Spawnia\Sailor\Operation
/**
* @param \Spawnia\Sailor\CustomTypes\Types\CustomEnum|null $value
*/
public static function execute($value = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.'): MyCustomEnumQuery\MyCustomEnumQueryResult
public static function execute(
$value = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.',
): MyCustomEnumQuery\MyCustomEnumQueryResult
{
return self::executeOperation(
$value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ class MyCustomEnumQuery extends \Spawnia\Sailor\ObjectLike
/**
* @param \Spawnia\Sailor\CustomTypes\Types\CustomEnum|null $withCustomEnum
*/
public static function make($withCustomEnum = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.'): self
public static function make(
$withCustomEnum = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.',
): self
{
$instance = new self;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ class MyCustomObjectQuery extends \Spawnia\Sailor\Operation
/**
* @param \Spawnia\Sailor\CustomTypesSrc\CustomObject|null $value
*/
public static function execute($value = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.'): MyCustomObjectQuery\MyCustomObjectQueryResult
public static function execute(
$value = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.',
): MyCustomObjectQuery\MyCustomObjectQueryResult
{
return self::executeOperation(
$value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ class MyCustomObjectQuery extends \Spawnia\Sailor\ObjectLike
/**
* @param \Spawnia\Sailor\CustomTypesSrc\CustomObject|null $withCustomObject
*/
public static function make($withCustomObject = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.'): self
public static function make(
$withCustomObject = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.',
): self
{
$instance = new self;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ class MyEnumInputQuery extends \Spawnia\Sailor\Operation
/**
* @param \Spawnia\Sailor\CustomTypes\Types\EnumInput|null $input
*/
public static function execute($input = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.'): MyEnumInputQuery\MyEnumInputQueryResult
public static function execute(
$input = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.',
): MyEnumInputQuery\MyEnumInputQueryResult
{
return self::executeOperation(
$input,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ class MyEnumInputQuery extends \Spawnia\Sailor\ObjectLike
/**
* @param \Spawnia\Sailor\CustomTypes\Operations\MyEnumInputQuery\WithEnumInput\EnumObject|null $withEnumInput
*/
public static function make($withEnumInput = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.'): self
public static function make(
$withEnumInput = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.',
): self
{
$instance = new self;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ class EnumObject extends \Spawnia\Sailor\ObjectLike
*/
public static function make(
$custom = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.',
$default = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.'
): self {
$default = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.',
): self
{
$instance = new self;

$instance->__typename = 'EnumObject';
Expand Down
50 changes: 50 additions & 0 deletions examples/custom-types/expected/Operations/MyNativeEnumQuery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

namespace Spawnia\Sailor\CustomTypes\Operations;

/**
* @extends \Spawnia\Sailor\Operation<\Spawnia\Sailor\CustomTypes\Operations\MyNativeEnumQuery\MyNativeEnumQueryResult>
*/
class MyNativeEnumQuery extends \Spawnia\Sailor\Operation
{
/**
* @param \Spawnia\Sailor\CustomTypes\Types\NativeEnum|null $value
*/
public static function execute(
$value = 'Special default value that allows Sailor to differentiate between explicitly passing null and not passing a value at all.',
): MyNativeEnumQuery\MyNativeEnumQueryResult
{
return self::executeOperation(
$value,
);
}

protected static function converters(): array
{
static $converters;

return $converters ??= [
['value', new \Spawnia\Sailor\Convert\NullConverter(new \Spawnia\Sailor\CustomTypes\TypeConverters\NativeEnumConverter)],
];
}

public static function document(): string
{
return /* @lang GraphQL */ 'query MyNativeEnumQuery($value: NativeEnum) {
__typename
withNativeEnum(value: $value)
}';
}

public static function endpoint(): string
{
return 'custom-types';
}

public static function config(): string
{
return \Safe\realpath(__DIR__ . '/../../sailor.php');
}
}
Loading