Skip to content

Commit b31292f

Browse files
committed
fix(graphql): enable graphql policies
1 parent 69c0cd2 commit b31292f

File tree

17 files changed

+422
-139
lines changed

17 files changed

+422
-139
lines changed

src/GraphQl/Type/FieldsBuilder.php

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818
use ApiPlatform\GraphQl\Exception\InvalidTypeException;
1919
use ApiPlatform\GraphQl\Resolver\Factory\ResolverFactoryInterface;
2020
use ApiPlatform\GraphQl\Type\Definition\TypeInterface;
21+
use ApiPlatform\Metadata\FilterInterface;
2122
use ApiPlatform\Metadata\GraphQl\Mutation;
2223
use ApiPlatform\Metadata\GraphQl\Operation;
2324
use ApiPlatform\Metadata\GraphQl\Query;
2425
use ApiPlatform\Metadata\GraphQl\Subscription;
2526
use ApiPlatform\Metadata\InflectorInterface;
27+
use ApiPlatform\Metadata\OpenApiParameterFilterInterface;
2628
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
2729
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
2830
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
@@ -296,20 +298,40 @@ public function resolveResourceArgs(array $args, Operation $operation): array
296298
continue;
297299
}
298300

301+
$filter = $this->filterLocator->get($filterId);
299302
$parsedKey = explode('[:property]', $key);
300303
$flattenFields = [];
301-
foreach ($this->filterLocator->get($filterId)->getDescription($operation->getClass()) as $key => $value) {
302-
$values = [];
303-
parse_str($key, $values);
304-
if (isset($values[$parsedKey[0]])) {
305-
$values = $values[$parsedKey[0]];
304+
305+
if ($filter instanceof FilterInterface) {
306+
foreach ($filter->getDescription($operation->getClass()) as $name => $value) {
307+
$values = [];
308+
parse_str($name, $values);
309+
if (isset($values[$parsedKey[0]])) {
310+
$values = $values[$parsedKey[0]];
311+
}
312+
313+
$name = key($values);
314+
$flattenFields[] = ['name' => $name, 'required' => $value['required'] ?? null, 'description' => $value['description'] ?? null, 'leafs' => $values[$name], 'type' => $value['type'] ?? 'string'];
315+
}
316+
317+
$args[$parsedKey[0]] = $this->parameterToObjectType($flattenFields, $parsedKey[0]);
318+
}
319+
320+
if ($filter instanceof OpenApiParameterFilterInterface) {
321+
foreach ($filter->getOpenApiParameters($parameter) as $value) {
322+
$values = [];
323+
parse_str($value->getName(), $values);
324+
if (isset($values[$parsedKey[0]])) {
325+
$values = $values[$parsedKey[0]];
326+
}
327+
328+
$name = key($values);
329+
$flattenFields[] = ['name' => $name, 'required' => $value->getRequired(), 'description' => $value->getDescription(), 'leafs' => $values[$name], 'type' => $value->getSchema()['type'] ?? 'string'];
306330
}
307331

308-
$name = key($values);
309-
$flattenFields[] = ['name' => $name, 'required' => $value['required'] ?? null, 'description' => $value['description'] ?? null, 'leafs' => $values[$name], 'type' => $value['type'] ?? 'string'];
332+
$args[$parsedKey[0]] = $this->parameterToObjectType($flattenFields, $parsedKey[0].$operation->getShortName().$operation->getName());
310333
}
311334

312-
$args[$parsedKey[0]] = $this->parameterToObjectType($flattenFields, $parsedKey[0]);
313335
continue;
314336
}
315337

src/Laravel/ApiPlatformProvider.php

Lines changed: 106 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,10 @@ public function register(): void
927927
);
928928
});
929929

930+
if (interface_exists(FieldsBuilderEnumInterface::class)) {
931+
$this->registerGraphQl($this->app);
932+
}
933+
930934
$this->app->singleton(JsonApiObjectNormalizer::class, function (Application $app) {
931935
return new JsonApiObjectNormalizer(
932936
$app->make(ObjectNormalizer::class),
@@ -936,51 +940,6 @@ public function register(): void
936940
);
937941
});
938942

939-
if ($this->app['config']->get('api-platform.graphql.enabled')) {
940-
$this->app->singleton(GraphQlItemNormalizer::class, function (Application $app) {
941-
return new GraphQlItemNormalizer(
942-
$app->make(PropertyNameCollectionFactoryInterface::class),
943-
$app->make(PropertyMetadataFactoryInterface::class),
944-
$app->make(IriConverterInterface::class),
945-
$app->make(IdentifiersExtractorInterface::class),
946-
$app->make(ResourceClassResolverInterface::class),
947-
$app->make(PropertyAccessorInterface::class),
948-
$app->make(NameConverterInterface::class),
949-
$app->make(SerializerClassMetadataFactory::class),
950-
null,
951-
$app->make(ResourceMetadataCollectionFactoryInterface::class),
952-
$app->make(ResourceAccessCheckerInterface::class)
953-
);
954-
});
955-
956-
$this->app->singleton(GraphQlObjectNormalizer::class, function (Application $app) {
957-
return new GraphQlObjectNormalizer(
958-
$app->make(ObjectNormalizer::class),
959-
$app->make(IriConverterInterface::class),
960-
$app->make(IdentifiersExtractorInterface::class),
961-
);
962-
});
963-
}
964-
965-
$this->app->singleton(GraphQlErrorNormalizer::class, function () {
966-
return new GraphQlErrorNormalizer();
967-
});
968-
969-
$this->app->singleton(GraphQlValidationExceptionNormalizer::class, function (Application $app) {
970-
/** @var ConfigRepository */
971-
$config = $app['config'];
972-
973-
return new GraphQlValidationExceptionNormalizer($config->get('api-platform.exception_to_status'));
974-
});
975-
976-
$this->app->singleton(GraphQlHttpExceptionNormalizer::class, function () {
977-
return new GraphQlHttpExceptionNormalizer();
978-
});
979-
980-
$this->app->singleton(GraphQlRuntimeExceptionNormalizer::class, function () {
981-
return new GraphQlHttpExceptionNormalizer();
982-
});
983-
984943
$this->app->bind(SerializerInterface::class, Serializer::class);
985944
$this->app->bind(NormalizerInterface::class, Serializer::class);
986945
$this->app->singleton(Serializer::class, function (Application $app) {
@@ -1009,7 +968,7 @@ public function register(): void
1009968
$list->insert($app->make(JsonApiItemNormalizer::class), -890);
1010969
$list->insert($app->make(JsonApiObjectNormalizer::class), -995);
1011970

1012-
if ($config->get('api-platform.graphql.enabled')) {
971+
if (interface_exists(FieldsBuilderEnumInterface::class)) {
1013972
$list->insert($app->make(GraphQlItemNormalizer::class), -890);
1014973
$list->insert($app->make(GraphQlObjectNormalizer::class), -995);
1015974
$list->insert($app->make(GraphQlErrorNormalizer::class), -790);
@@ -1033,7 +992,8 @@ public function register(): void
1033992
new JsonEncoder('jsonapi'),
1034993
new JsonEncoder('jsonhal'),
1035994
new CsvEncoder(),
1036-
]);
995+
]
996+
);
1037997
});
1038998

1039999
$this->app->singleton(JsonLdItemNormalizer::class, function (Application $app) {
@@ -1078,17 +1038,56 @@ function (Application $app) {
10781038
return new Inflector();
10791039
});
10801040

1081-
if ($this->app['config']->get('api-platform.graphql.enabled')) {
1082-
$this->registerGraphQl($this->app);
1083-
}
1084-
10851041
if ($this->app->runningInConsole()) {
10861042
$this->commands([Console\InstallCommand::class]);
10871043
}
10881044
}
10891045

10901046
private function registerGraphQl(Application $app): void
10911047
{
1048+
$this->app->singleton(GraphQlItemNormalizer::class, function (Application $app) {
1049+
return new GraphQlItemNormalizer(
1050+
$app->make(PropertyNameCollectionFactoryInterface::class),
1051+
$app->make(PropertyMetadataFactoryInterface::class),
1052+
$app->make(IriConverterInterface::class),
1053+
$app->make(IdentifiersExtractorInterface::class),
1054+
$app->make(ResourceClassResolverInterface::class),
1055+
$app->make(PropertyAccessorInterface::class),
1056+
$app->make(NameConverterInterface::class),
1057+
$app->make(SerializerClassMetadataFactory::class),
1058+
null,
1059+
$app->make(ResourceMetadataCollectionFactoryInterface::class),
1060+
$app->make(ResourceAccessCheckerInterface::class)
1061+
);
1062+
});
1063+
1064+
$this->app->singleton(GraphQlObjectNormalizer::class, function (Application $app) {
1065+
return new GraphQlObjectNormalizer(
1066+
$app->make(ObjectNormalizer::class),
1067+
$app->make(IriConverterInterface::class),
1068+
$app->make(IdentifiersExtractorInterface::class),
1069+
);
1070+
});
1071+
1072+
$this->app->singleton(GraphQlErrorNormalizer::class, function () {
1073+
return new GraphQlErrorNormalizer();
1074+
});
1075+
1076+
$this->app->singleton(GraphQlValidationExceptionNormalizer::class, function (Application $app) {
1077+
/** @var ConfigRepository */
1078+
$config = $app['config'];
1079+
1080+
return new GraphQlValidationExceptionNormalizer($config->get('api-platform.exception_to_status'));
1081+
});
1082+
1083+
$this->app->singleton(GraphQlHttpExceptionNormalizer::class, function () {
1084+
return new GraphQlHttpExceptionNormalizer();
1085+
});
1086+
1087+
$this->app->singleton(GraphQlRuntimeExceptionNormalizer::class, function () {
1088+
return new GraphQlHttpExceptionNormalizer();
1089+
});
1090+
10921091
$app->singleton('api_platform.graphql.type_locator', function (Application $app) {
10931092
$tagged = iterator_to_array($app->tagged('api_platform.graphql.type'));
10941093

@@ -1130,44 +1129,78 @@ private function registerGraphQl(Application $app): void
11301129
return new GraphQlSerializerContextBuilder($app->make(NameConverterInterface::class));
11311130
});
11321131

1133-
$app->singleton('api_platform.graphql.state_provider', function (Application $app) {
1132+
$app->singleton(GraphQlReadProvider::class, function (Application $app) {
11341133
/** @var ConfigRepository */
11351134
$config = $app['config'];
1136-
$tagged = iterator_to_array($app->tagged(ParameterProviderInterface::class));
1137-
$resolvers = iterator_to_array($app->tagged('api_platform.graphql.resolver'));
11381135

11391136
return new GraphQlReadProvider(
1140-
new GraphQlDenormalizeProvider(
1141-
new ResolverProvider(
1142-
new ParameterProvider(
1143-
$app->make(CallableProvider::class),
1144-
new ServiceLocator($tagged)
1145-
),
1146-
new ServiceLocator($resolvers),
1147-
),
1148-
$app->make(SerializerInterface::class),
1149-
$app->make(GraphQlSerializerContextBuilder::class)
1150-
),
1137+
$this->app->make(CallableProvider::class),
11511138
$app->make(IriConverterInterface::class),
11521139
$app->make(GraphQlSerializerContextBuilder::class),
11531140
$config->get('api-platform.graphql.nesting_separator') ?? '__'
11541141
);
11551142
});
1143+
$app->alias(GraphQlReadProvider::class, 'api_platform.graphql.state_provider.read');
1144+
1145+
$app->singleton(ResolverProvider::class, function (Application $app) {
1146+
$resolvers = iterator_to_array($app->tagged('api_platform.graphql.resolver'));
1147+
1148+
return new ResolverProvider(
1149+
$app->make(GraphQlReadProvider::class),
1150+
new ServiceLocator($resolvers),
1151+
);
1152+
});
1153+
1154+
$app->alias(ResolverProvider::class, 'api_platform.graphql.state_provider.resolver');
1155+
1156+
$app->singleton(GraphQlDenormalizeProvider::class, function (Application $app) {
1157+
return new GraphQlDenormalizeProvider(
1158+
$this->app->make(ResolverProvider::class),
1159+
$app->make(SerializerInterface::class),
1160+
$app->make(GraphQlSerializerContextBuilder::class)
1161+
);
1162+
});
1163+
1164+
$app->alias(GraphQlDenormalizeProvider::class, 'api_platform.graphql.state_provider.denormalize');
1165+
1166+
$app->singleton('api_platform.graphql.state_provider.parameter', function (Application $app) {
1167+
$tagged = iterator_to_array($app->tagged(ParameterProviderInterface::class));
1168+
$tagged['api_platform.serializer.filter_parameter_provider'] = $app->make(SerializerFilterParameterProvider::class);
1169+
1170+
return new ParameterProvider(
1171+
new ParameterValidatorProvider(
1172+
new SecurityParameterProvider(
1173+
$app->make(GraphQlDenormalizeProvider::class),
1174+
$app->make(ResourceAccessCheckerInterface::class)
1175+
),
1176+
),
1177+
new ServiceLocator($tagged)
1178+
);
1179+
});
1180+
1181+
$app->singleton('api_platform.graphql.state_provider.access_checker', function (Application $app) {
1182+
return new AccessCheckerProvider($app->make('api_platform.graphql.state_provider.parameter'), $app->make(ResourceAccessCheckerInterface::class));
1183+
});
1184+
1185+
$app->singleton(NormalizeProcessor::class, function (Application $app) {
1186+
return new NormalizeProcessor(
1187+
$app->make(SerializerInterface::class),
1188+
$app->make(GraphQlSerializerContextBuilder::class),
1189+
$app->make(Pagination::class)
1190+
);
1191+
});
1192+
$app->alias(NormalizeProcessor::class, 'api_platform.graphql.state_processor.normalize');
11561193

11571194
$app->singleton('api_platform.graphql.state_processor', function (Application $app) {
11581195
return new WriteProcessor(
1159-
new NormalizeProcessor(
1160-
$app->make(SerializerInterface::class),
1161-
$app->make(GraphQlSerializerContextBuilder::class),
1162-
$app->make(Pagination::class)
1163-
),
1196+
$app->make('api_platform.graphql.state_processor.normalize'),
11641197
$app->make(CallableProcessor::class),
11651198
);
11661199
});
11671200

11681201
$app->singleton(ResolverFactoryInterface::class, function (Application $app) {
11691202
return new ResolverFactory(
1170-
$app->make('api_platform.graphql.state_provider'),
1203+
$app->make('api_platform.graphql.state_provider.access_checker'),
11711204
$app->make('api_platform.graphql.state_processor')
11721205
);
11731206
});
@@ -1227,7 +1260,8 @@ private function registerGraphQl(Application $app): void
12271260
$app->make(SerializerInterface::class),
12281261
$app->make(ErrorHandlerInterface::class),
12291262
debug: $config->get('app.debug'),
1230-
negotiator: $app->make(Negotiator::class)
1263+
negotiator: $app->make(Negotiator::class),
1264+
formats: $config->get('api-platform.formats')
12311265
);
12321266
});
12331267
}

src/Laravel/Eloquent/Metadata/Factory/Resource/EloquentResourceCollectionMetadataFactory.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
use ApiPlatform\Metadata\DeleteOperationInterface;
2323
use ApiPlatform\Metadata\Get;
2424
use ApiPlatform\Metadata\GetCollection;
25+
use ApiPlatform\Metadata\GraphQl\DeleteMutation;
26+
use ApiPlatform\Metadata\GraphQl\Mutation;
27+
use ApiPlatform\Metadata\GraphQl\Query;
28+
use ApiPlatform\Metadata\GraphQl\QueryCollection;
29+
use ApiPlatform\Metadata\GraphQl\Subscription;
2530
use ApiPlatform\Metadata\Patch;
2631
use ApiPlatform\Metadata\Post;
2732
use ApiPlatform\Metadata\Put;
@@ -39,6 +44,12 @@ final class EloquentResourceCollectionMetadataFactory implements ResourceMetadat
3944
GetCollection::class => 'viewAny',
4045
Delete::class => 'delete',
4146
Patch::class => 'update',
47+
48+
Query::class => 'view',
49+
QueryCollection::class => 'viewAny',
50+
Mutation::class => 'update',
51+
DeleteMutation::class => 'delete',
52+
Subscription::class => 'viewAny',
4253
];
4354

4455
public function __construct(
@@ -94,6 +105,12 @@ public function create(string $resourceClass): ResourceMetadataCollection
94105
$graphQlOperations = $resourceMetadata->getGraphQlOperations();
95106

96107
foreach ($graphQlOperations ?? [] as $operationName => $graphQlOperation) {
108+
if (!$graphQlOperation->getPolicy() && ($policy = Gate::getPolicyFor($model))) {
109+
if (($policyMethod = self::POLICY_METHODS[$graphQlOperation::class] ?? null) && method_exists($policy, $policyMethod)) {
110+
$graphQlOperation = $graphQlOperation->withPolicy($policyMethod);
111+
}
112+
}
113+
97114
if (!$graphQlOperation->getProvider()) {
98115
$graphQlOperation = $graphQlOperation->withProvider($graphQlOperation instanceof CollectionOperationInterface ? CollectionProvider::class : ItemProvider::class);
99116
}

0 commit comments

Comments
 (0)