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
3 changes: 2 additions & 1 deletion .commitlintrc
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"openapi",
"serializer",
"jsonschema",
"validation"
"validation",
"state"
]
],
"scope-empty": [
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ jobs:
- name: Run commitlint
run: |
commit=$(gh api \
-H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/pulls/${{github.event.number}}/commits \
| jq -r '.[0].commit.message')
| jq -r '.[0].commit.message' \
| head -n 1)
# we can't use npx see https://github.com/conventional-changelog/commitlint/issues/613
echo '{}' > package.json
npm install --no-fund --no-audit @commitlint/config-conventional @commitlint/cli
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 3.0.2

* Metadata: generate skolem IRI by default, use `genId: false` to disable **BC**

## 3.0.1

* Symfony: don't use ArrayAdapter cache in production #5027
Expand Down Expand Up @@ -50,6 +54,12 @@ Breaking changes:
* Serializer: `skip_null_values` now defaults to `true`
* Metadata: `Patch` is added to the automatic CRUD

## 2.7.2

* Metadata: no skolem IRI by default
* Symfony: use service id as tag for lower symfony versions (processor/provider service locator)
* Symfony: fix command constants not available on lower symfony versions

## 2.7.1

* Chore: update swagger ui and javascript libraries (#5028)
Expand Down
9 changes: 9 additions & 0 deletions features/jsonld/non_resource.feature
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Feature: JSON-LD non-resource handling
}
}
"""
And the JSON node "notAResource.@id" should exist

Scenario: Get a resource containing a raw object with selected properties
Given there are 1 dummy objects with relatedDummy and its thirdLevel
Expand Down Expand Up @@ -123,3 +124,11 @@ Feature: JSON-LD non-resource handling
"id": 1
}
"""

@php8
Scenario: Get a generated id
When I send a "GET" request to "/genids/1"
Then the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the JSON node "totalPrice.@id" should not exist
2 changes: 2 additions & 0 deletions src/GraphQl/Serializer/ItemNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ public function supportsNormalization(mixed $data, string $format = null, array
/**
* {@inheritdoc}
*
* @param array<string, mixed> $context
*
* @throws UnexpectedValueException
*/
public function normalize(mixed $object, string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null
Expand Down
1 change: 1 addition & 0 deletions src/Hydra/Serializer/CollectionNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public function normalize(mixed $object, string $format = null, array $context =
{
$resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class']);
$context = $this->initContext($resourceClass, $context);
$context['api_collection_sub_level'] = true;
$data = $this->addJsonLdContext($this->contextBuilder, $resourceClass, $context);
$data['@id'] = $this->iriConverter->getIriFromResource($resourceClass, UrlGeneratorInterface::ABS_PATH, $context['operation'] ?? null, $context);
$data['@type'] = 'hydra:Collection';
Expand Down
11 changes: 4 additions & 7 deletions src/JsonLd/ContextBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

use ApiPlatform\Api\IriConverterInterface;
use ApiPlatform\Api\UrlGeneratorInterface;
use ApiPlatform\Exception\RuntimeException;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\HttpOperation;
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
Expand Down Expand Up @@ -123,12 +122,10 @@ public function getAnonymousResourceContext(object $object, array $context = [],
'@type' => $shortName,
];

if (!isset($context['iri']) || false !== $context['iri']) {
if (!isset($context['iri']) && !$this->iriConverter) {
throw new RuntimeException('Can not guess an anonymous resource IRI without IRI Converter.');
}

$jsonLdContext['@id'] = $context['iri'] ?? $this->iriConverter->getIriFromResource($object);
if (isset($context['iri'])) {
$jsonLdContext['@id'] = $context['iri'];
} elseif (true === ($context['gen_id'] ?? true) && $this->iriConverter) {
$jsonLdContext['@id'] = $this->iriConverter->getIriFromResource($object);
}

if ($context['has_context'] ?? false) {
Expand Down
6 changes: 6 additions & 0 deletions src/JsonLd/Serializer/ItemNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ public function normalize(mixed $object, string $format = null, array $context =
$context = $this->initContext($resourceClass, $context);
$metadata = $this->addJsonLdContext($this->contextBuilder, $resourceClass, $context);
} elseif ($this->contextBuilder instanceof AnonymousContextBuilderInterface) {
if ($context['api_collection_sub_level'] ?? false) {
unset($context['api_collection_sub_level']);
$context['output']['genid'] = true;
$context['output']['iri'] = null;
}

// We should improve what's behind the context creation, its probably more complicated then it should
$metadata = $this->createJsonLdContext($this->contextBuilder, $object, $context);
}
Expand Down
4 changes: 2 additions & 2 deletions src/JsonLd/Serializer/JsonLdContextTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ private function createJsonLdContext(AnonymousContextBuilderInterface $contextBu
{
// We're in a collection, don't add the @context part
if (isset($context['jsonld_has_context'])) {
return $contextBuilder->getAnonymousResourceContext($object, ['api_resource' => $context['api_resource'] ?? null, 'has_context' => true]);
return $contextBuilder->getAnonymousResourceContext($object, ($context['output'] ?? []) + ['api_resource' => $context['api_resource'] ?? null, 'has_context' => true]);
}

$context['jsonld_has_context'] = true;

return $contextBuilder->getAnonymousResourceContext($object, ['api_resource' => $context['api_resource'] ?? null]);
return $contextBuilder->getAnonymousResourceContext($object, ($context['output'] ?? []) + ['api_resource' => $context['api_resource'] ?? null]);
}
}
17 changes: 17 additions & 0 deletions src/Metadata/ApiProperty.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public function __construct(
private ?array $schema = null,
private ?bool $initializable = null,
private $iris = null,
private ?bool $genId = null,
private array $extraProperties = []
) {
if (\is_string($types)) {
Expand Down Expand Up @@ -403,4 +404,20 @@ public function withIris(string|array $iris): self

return $metadata;
}

/**
* Whether to generate a skolem iri on anonymous resources.
*/
public function getGenId()
{
return $this->genId;
}

public function withGenId(bool $genId): self
{
$metadata = clone $this;
$metadata->genId = $genId;

return $metadata;
}
}
1 change: 1 addition & 0 deletions src/Metadata/Extractor/XmlPropertyExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ protected function extractPath(string $path): void
'initializable' => $this->phpize($property, 'initializable', 'bool'),
'extraProperties' => $this->buildExtraProperties($property, 'extraProperties'),
'iris' => $this->buildArrayValue($property, 'iri'),
'genId' => $this->phpize($property, 'genId', 'bool'),
];
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Metadata/Extractor/YamlPropertyExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ private function buildProperties(array $resourcesYaml): void
'example' => $propertyValues['example'] ?? null,
'builtinTypes' => $this->buildAttribute($propertyValues, 'builtinTypes'),
'schema' => $this->buildAttribute($propertyValues, 'schema'),
'genId' => $this->phpize($propertyValues, 'genId', 'bool'),
];
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Metadata/Extractor/schema/properties.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
<xsd:attribute name="security" type="xsd:string"/>
<xsd:attribute name="securityPostDenormalize" type="xsd:string"/>
<xsd:attribute name="initializable" type="xsd:boolean"/>
<xsd:attribute name="genId" type="xsd:boolean"/>
</xsd:complexType>

<xsd:complexType name="types">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@

/**
* @author Antoine Bluchet <soyuka@gmail.com>
*
* @deprecated BC layer, is removed in 3.0
*/
final class AlternateUriResourceMetadataCollectionFactory implements ResourceMetadataCollectionFactoryInterface
{
Expand Down
5 changes: 1 addition & 4 deletions src/Serializer/AbstractItemNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -592,10 +592,7 @@ protected function getAttributeValue(object $object, string $attribute, string $
if ($type && $type->getClassName()) {
$childContext = $this->createChildContext($context, $attribute, $format);
unset($childContext['iri'], $childContext['uri_variables']);

if (null !== ($propertyIris = $propertyMetadata->getIris())) {
$childContext['output']['iri'] = 1 === (is_countable($propertyIris) ? \count($propertyIris) : 0) ? $propertyIris[0] : $propertyIris;
}
$childContext['output']['gen_id'] = $propertyMetadata->getGenId() ?? true;

return $this->serializer->normalize($attributeValue, $format, $childContext);
}
Expand Down
7 changes: 6 additions & 1 deletion src/State/CreateProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace ApiPlatform\State;

use ApiPlatform\Exception\RuntimeException;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\HttpOperation;
use ApiPlatform\Metadata\Link;
Expand Down Expand Up @@ -58,7 +59,11 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
}

$relation = $this->decorated->provide(new Get(uriVariables: $relationUriVariables, class: $relationClass), $uriVariables);
$resource = new ($operation->getClass());
try {
$resource = new ($operation->getClass());
} catch (\Throwable $e) {
throw new RuntimeException(sprintf('An error occurred while trying to create an instance of the "%s" resource. Consider writing your own "%s" implementation and setting it as `provider` on your operation instead.', $operation->getClass(), ProviderInterface::class), 0, $e);
}
$this->propertyAccessor->setValue($resource, $key, $relation);

return $resource;
Expand Down
36 changes: 36 additions & 0 deletions src/State/ObjectProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\State;

use ApiPlatform\Exception\RuntimeException;
use ApiPlatform\Metadata\Operation;

/**
* An ItemProvider that just creates a new object.
*
* @author Antoine Bluchet <soyuka@gmail.com>
*
* @experimental
*/
final class ObjectProvider implements ProviderInterface
{
public function provide(Operation $operation, array $uriVariables = [], array $context = []): ?object
{
try {
return new ($operation->getClass());
} catch (\Throwable $e) {
throw new RuntimeException(sprintf('An error occurred while trying to create an instance of the "%s" resource. Consider writing your own "%s" implementation and setting it as `provider` on your operation instead.', $operation->getClass(), ProviderInterface::class), 0, $e);
}
}
}
6 changes: 3 additions & 3 deletions src/Symfony/Bundle/Command/DebugResourceCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
if (0 === \count($resourceCollection)) {
$output->writeln(sprintf('<error>No resources found for class %s</error>', $resourceClass));

return Command::INVALID;
return \defined(Command::class.'::INVALID') ? Command::INVALID : 2;
}

$shortName = (false !== $pos = strrpos($resourceClass, '\\')) ? substr($resourceClass, $pos + 1) : $resourceClass;
Expand Down Expand Up @@ -103,13 +103,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$this->dumper->dump($this->cloner->cloneVar($selectedResource));
$output->writeln('Successfully dumped the selected resource');

return Command::SUCCESS;
return \defined(Command::class.'::SUCCESS') ? Command::SUCCESS : 0;
}

$this->dumper->dump($this->cloner->cloneVar($resourceCollection->getOperation($answerOperation)));
$output->writeln('Successfully dumped the selected operation');

return Command::SUCCESS;
return \defined(Command::class.'::SUCCESS') ? Command::SUCCESS : 0;
}

public static function getDefaultName(): string
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Bundle/Command/GraphQlExportCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$output->writeln($schemaExport);
}

return 0;
return \defined(Command::class.'::SUCCESS') ? Command::SUCCESS : 0;
}

public static function getDefaultName(): string
Expand Down
4 changes: 2 additions & 2 deletions src/Symfony/Bundle/Command/OpenApiCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$filesystem->dumpFile($filename, $content);
$io->success(sprintf('Data written to %s.', $filename));

return 0;
return \defined(Command::class.'::SUCCESS') ? Command::SUCCESS : 0;
}

$output->writeln($content);

return 0;
return \defined(Command::class.'::SUCCESS') ? Command::SUCCESS : 0;
}

public static function getDefaultName(): string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ public function load(array $configs, ContainerBuilder $container): void
->addTag('api_platform.state_provider');
$container->registerForAutoconfiguration(ProcessorInterface::class)
->addTag('api_platform.state_processor');

if (!$container->has('api_platform.state.item_provider')) {
$container->setAlias('api_platform.state.item_provider', 'api_platform.state_provider.object');
}
}

private function registerCommonConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader, array $formats, array $patchFormats, array $errorFormats): void
Expand Down
4 changes: 2 additions & 2 deletions src/Symfony/Bundle/Resources/config/doctrine_mongodb_odm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@
<service id="api_platform.doctrine_mongodb.odm.state.remove_processor" class="ApiPlatform\Doctrine\Common\State\RemoveProcessor">
<argument type="service" id="doctrine_mongodb" />

<tag name="api_platform.state_processor" priority="-100" key="ApiPlatform\Doctrine\Common\State\RemoveProcessor" />
<tag name="api_platform.state_processor" priority="-100" key="api_platform.doctrine_mongodb.odm.state.remove_processor" />
<tag name="api_platform.state_processor" priority="-100" key="ApiPlatform\Doctrine\Common\State\RemoveProcessor" />
</service>
<service id="ApiPlatform\Doctrine\Common\State\RemoveProcessor" alias="api_platform.doctrine_mongodb.odm.state.remove_processor" />

<service id="api_platform.doctrine_mongodb.odm.state.persist_processor" class="ApiPlatform\Doctrine\Common\State\PersistProcessor">
<argument type="service" id="doctrine_mongodb" />

<tag name="api_platform.state_processor" priority="-100" key="ApiPlatform\Doctrine\Common\State\PersistProcessor" />
<tag name="api_platform.state_processor" priority="-100" key="api_platform.doctrine_mongodb.odm.state.persist_processor" />
<tag name="api_platform.state_processor" priority="-100" key="ApiPlatform\Doctrine\Common\State\PersistProcessor" />
</service>
<service id="ApiPlatform\Doctrine\Common\State\PersistProcessor" alias="api_platform.doctrine_mongodb.odm.state.persist_processor" />

Expand Down
4 changes: 2 additions & 2 deletions src/Symfony/Bundle/Resources/config/doctrine_orm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@
<service id="api_platform.doctrine.orm.state.remove_processor" class="ApiPlatform\Doctrine\Common\State\RemoveProcessor">
<argument type="service" id="doctrine" />

<tag name="api_platform.state_processor" priority="-100" key="ApiPlatform\Doctrine\Common\State\RemoveProcessor" />
<tag name="api_platform.state_processor" priority="-100" key="api_platform.doctrine.orm.state.remove_processor" />
<tag name="api_platform.state_processor" priority="-100" key="ApiPlatform\Doctrine\Common\State\RemoveProcessor" />
</service>
<service id="ApiPlatform\Doctrine\Common\State\RemoveProcessor" alias="api_platform.doctrine.orm.state.remove_processor" />

<service id="api_platform.doctrine.orm.state.persist_processor" class="ApiPlatform\Doctrine\Common\State\PersistProcessor">
<argument type="service" id="doctrine" />

<tag name="api_platform.state_processor" priority="-100" key="ApiPlatform\Doctrine\Common\State\PersistProcessor" />
<tag name="api_platform.state_processor" priority="-100" key="api_platform.doctrine.orm.state.persist_processor" />
<tag name="api_platform.state_processor" priority="-100" key="ApiPlatform\Doctrine\Common\State\PersistProcessor" />
</service>
<service id="ApiPlatform\Doctrine\Common\State\PersistProcessor" alias="api_platform.doctrine.orm.state.persist_processor" />

Expand Down
14 changes: 10 additions & 4 deletions src/Symfony/Bundle/Resources/config/state.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,18 @@
</service>
<service id="ApiPlatform\State\Pagination\PaginationOptions" alias="api_platform.pagination_options" />

<service id="ApiPlatform\State\CreateProvider" class="ApiPlatform\State\CreateProvider">
<argument type="service" id="api_platform.state.item_provider" on-invalid="ignore" />
<service id="api_platform.state_provider.create" class="ApiPlatform\State\CreateProvider">
<argument type="service" id="api_platform.state.item_provider" />

<tag name="api_platform.state_provider" />
<tag name="api_platform.state_provider" key="ApiPlatform\State\CreateProvider" />
<tag name="api_platform.state_provider" key="api_platform.state_provider.create" />
</service>
<service id="api_platform.state_provider.create" alias="ApiPlatform\State\CreateProvider" />
<service id="ApiPlatform\State\CreateProvider" alias="api_platform.state_provider.create" />

<service id="api_platform.state_provider.object" class="ApiPlatform\State\ObjectProvider">
<tag name="api_platform.state_provider" key="ApiPlatform\State\ObjectProvider" />
<tag name="api_platform.state_provider" key="api_platform.state_provider.object" />
</service>
<service id="ApiPlatform\State\ObjectProvider" alias="api_platform.state_provider.object" />
</services>
</container>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading