From 5e925f9d39e3f179d1d9f787aa3f6c21a5b98229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Fri, 26 Dec 2025 14:45:04 +0100 Subject: [PATCH] Implement the Union Shape fix #1900 --- manifest.json | 24 +- .../src/Definition/ObjectShape.php | 12 + src/CodeGenerator/src/Definition/Shape.php | 2 +- .../src/Definition/StructureMember.php | 2 +- .../src/Definition/StructureShape.php | 4 +- .../src/Definition/UnionShape.php | 53 +++++ .../CodeGenerator/PopulatorGenerator.php | 12 +- .../Generator/CodeGenerator/TypeGenerator.php | 161 ++++++++------ .../src/Generator/EnumGenerator.php | 67 +----- .../src/Generator/InputGenerator.php | 27 ++- .../src/Generator/ObjectGenerator.php | 210 +++++++++++++----- .../Generator/PhpGenerator/ClassBuilder.php | 8 + .../RequestSerializer/QuerySerializer.php | 21 +- .../RequestSerializer/RestJsonSerializer.php | 21 +- .../RequestSerializer/RestXmlSerializer.php | 14 +- .../RequestSerializer/Serializer.php | 6 +- .../RequestSerializer/SerializerResult.php | 19 +- .../SerializerResultBody.php | 16 +- .../SerializerResultBuilder.php | 27 --- .../ResponseParser/RestJsonParser.php | 89 +++++--- .../ResponseParser/RestXmlParser.php | 61 ++++- .../src/Generator/ResultGenerator.php | 7 +- .../src/Generator/ServiceGenerator.php | 18 +- .../src/Generator/ShapeUsageHelper.php | 116 ++++++++++ src/Service/RdsDataService/CHANGELOG.md | 4 + src/Service/RdsDataService/composer.json | 2 +- .../Result/BatchExecuteStatementResponse.php | 162 ++++++++++++-- .../src/Result/ExecuteStatementResponse.php | 162 ++++++++++++-- .../src/ValueObject/ArrayValue.php | 177 +++------------ .../ArrayValueMemberArrayValues.php | 59 +++++ .../ArrayValueMemberBooleanValues.php | 59 +++++ .../ArrayValueMemberDoubleValues.php | 59 +++++ .../ArrayValueMemberLongValues.php | 59 +++++ .../ArrayValueMemberStringValues.php | 59 +++++ .../ArrayValueMemberUnknownToSdk.php | 39 ++++ .../RdsDataService/src/ValueObject/Field.php | 179 ++++----------- .../src/ValueObject/FieldMemberArrayValue.php | 50 +++++ .../src/ValueObject/FieldMemberBlobValue.php | 50 +++++ .../ValueObject/FieldMemberBooleanValue.php | 50 +++++ .../ValueObject/FieldMemberDoubleValue.php | 50 +++++ .../src/ValueObject/FieldMemberIsNull.php | 50 +++++ .../src/ValueObject/FieldMemberLongValue.php | 50 +++++ .../ValueObject/FieldMemberStringValue.php | 50 +++++ .../ValueObject/FieldMemberUnknownToSdk.php | 39 ++++ src/Service/S3Vectors/CHANGELOG.md | 4 + src/Service/S3Vectors/composer.json | 2 +- .../S3Vectors/src/Result/GetVectorsOutput.php | 20 +- .../src/Result/ListVectorsOutput.php | 20 +- .../S3Vectors/src/ValueObject/VectorData.php | 56 ++--- .../ValueObject/VectorDataMemberFloat32.php | 60 +++++ .../VectorDataMemberUnknownToSdk.php | 39 ++++ .../tests/Integration/S3VectorsClientTest.php | 3 +- .../tests/Unit/Input/PutVectorsInputTest.php | 4 +- .../Unit/Input/QueryVectorsInputTest.php | 4 +- .../tests/Unit/S3VectorsClientTest.php | 8 +- 55 files changed, 1947 insertions(+), 679 deletions(-) create mode 100644 src/CodeGenerator/src/Definition/ObjectShape.php create mode 100644 src/CodeGenerator/src/Definition/UnionShape.php delete mode 100644 src/CodeGenerator/src/Generator/RequestSerializer/SerializerResultBuilder.php create mode 100644 src/CodeGenerator/src/Generator/ShapeUsageHelper.php create mode 100644 src/Service/RdsDataService/src/ValueObject/ArrayValueMemberArrayValues.php create mode 100644 src/Service/RdsDataService/src/ValueObject/ArrayValueMemberBooleanValues.php create mode 100644 src/Service/RdsDataService/src/ValueObject/ArrayValueMemberDoubleValues.php create mode 100644 src/Service/RdsDataService/src/ValueObject/ArrayValueMemberLongValues.php create mode 100644 src/Service/RdsDataService/src/ValueObject/ArrayValueMemberStringValues.php create mode 100644 src/Service/RdsDataService/src/ValueObject/ArrayValueMemberUnknownToSdk.php create mode 100644 src/Service/RdsDataService/src/ValueObject/FieldMemberArrayValue.php create mode 100644 src/Service/RdsDataService/src/ValueObject/FieldMemberBlobValue.php create mode 100644 src/Service/RdsDataService/src/ValueObject/FieldMemberBooleanValue.php create mode 100644 src/Service/RdsDataService/src/ValueObject/FieldMemberDoubleValue.php create mode 100644 src/Service/RdsDataService/src/ValueObject/FieldMemberIsNull.php create mode 100644 src/Service/RdsDataService/src/ValueObject/FieldMemberLongValue.php create mode 100644 src/Service/RdsDataService/src/ValueObject/FieldMemberStringValue.php create mode 100644 src/Service/RdsDataService/src/ValueObject/FieldMemberUnknownToSdk.php create mode 100644 src/Service/S3Vectors/src/ValueObject/VectorDataMemberFloat32.php create mode 100644 src/Service/S3Vectors/src/ValueObject/VectorDataMemberUnknownToSdk.php diff --git a/manifest.json b/manifest.json index 0c17c482e..10264ab18 100644 --- a/manifest.json +++ b/manifest.json @@ -473,19 +473,7 @@ "CommitTransaction", "ExecuteStatement", "RollbackTransaction" - ], - "patches": { - "source": [ - { - "op": "remove", - "path": "/shapes/ArrayValue/union" - }, - { - "op": "remove", - "path": "/shapes/Field/union" - } - ] - } + ] }, "Rekognition": { "source": "https://raw.githubusercontent.com/aws/aws-sdk-php/${LATEST}/src/data/rekognition/2016-06-27/api-2.json", @@ -611,15 +599,7 @@ "GetVectors", "DeleteVectors", "QueryVectors" - ], - "patches": { - "source": [ - { - "op": "remove", - "path": "/shapes/VectorData/union" - } - ] - } + ] }, "Scheduler": { "source": "https://raw.githubusercontent.com/aws/aws-sdk-php/${LATEST}/src/data/scheduler/2021-06-30/api-2.json", diff --git a/src/CodeGenerator/src/Definition/ObjectShape.php b/src/CodeGenerator/src/Definition/ObjectShape.php new file mode 100644 index 000000000..5aac0ff01 --- /dev/null +++ b/src/CodeGenerator/src/Definition/ObjectShape.php @@ -0,0 +1,12 @@ +data['_name']; } - public function getOwnerShape(): StructureShape + public function getOwnerShape(): ObjectShape { return $this->data['_owner']; } diff --git a/src/CodeGenerator/src/Definition/StructureShape.php b/src/CodeGenerator/src/Definition/StructureShape.php index 20cdcba27..b03789d60 100644 --- a/src/CodeGenerator/src/Definition/StructureShape.php +++ b/src/CodeGenerator/src/Definition/StructureShape.php @@ -7,7 +7,7 @@ /** * @internal */ -class StructureShape extends Shape +class StructureShape extends ObjectShape { /** * @return StructureMember[] @@ -18,7 +18,7 @@ public function getMembers(): array $members = []; foreach ($this->data['members'] as $name => $member) { $members[] = new StructureMember( - $member + ['_name' => $name, '_owner' => $this, '_required' => \in_array($name, $required)], + $member + ['_name' => $name, '_owner' => $this->data['_members_owner'] ?? $this, '_required' => \in_array($name, $required)], $this->shapeLocator ); } diff --git a/src/CodeGenerator/src/Definition/UnionShape.php b/src/CodeGenerator/src/Definition/UnionShape.php new file mode 100644 index 000000000..4705506d8 --- /dev/null +++ b/src/CodeGenerator/src/Definition/UnionShape.php @@ -0,0 +1,53 @@ +data['members'] as $name => $member) { + $locationName = $member['locationName'] ?? $name; + $children[$locationName] = Shape::create( + $this->getChildName($name), + [ + 'type' => 'structure', + '_members_owner' => $this, + 'required' => [$name], + 'members' => [$name => $member], + ], + $this->shapeLocator, + $this->serviceLocator, + ); + } + + return $children; + } + + public function getChildForUnknown(): StructureShape + { + /** @phpstan-ignore return.type */ + return Shape::create( + $this->getChildName('UnknownToSdk'), + [ + 'type' => 'structure', + '_members_owner' => $this, + 'required' => [], + 'members' => [], + ], + $this->shapeLocator, + $this->serviceLocator, + ); + } + + private function getChildName(string $name): string + { + return $this->getName() . 'Member' . ucfirst($name); + } +} diff --git a/src/CodeGenerator/src/Generator/CodeGenerator/PopulatorGenerator.php b/src/CodeGenerator/src/Generator/CodeGenerator/PopulatorGenerator.php index 1d637090d..9afbf5ab2 100644 --- a/src/CodeGenerator/src/Generator/CodeGenerator/PopulatorGenerator.php +++ b/src/CodeGenerator/src/Generator/CodeGenerator/PopulatorGenerator.php @@ -7,6 +7,7 @@ use AsyncAws\CodeGenerator\Definition\DocumentShape; use AsyncAws\CodeGenerator\Definition\ListShape; use AsyncAws\CodeGenerator\Definition\MapShape; +use AsyncAws\CodeGenerator\Definition\ObjectShape; use AsyncAws\CodeGenerator\Definition\Operation; use AsyncAws\CodeGenerator\Definition\Shape; use AsyncAws\CodeGenerator\Definition\StructureShape; @@ -18,6 +19,7 @@ use AsyncAws\CodeGenerator\Generator\PhpGenerator\ClassBuilder; use AsyncAws\CodeGenerator\Generator\PhpGenerator\ClassRegistry; use AsyncAws\CodeGenerator\Generator\ResponseParser\ParserProvider; +use AsyncAws\CodeGenerator\Generator\ShapeUsageHelper; use AsyncAws\Core\Response; use AsyncAws\Core\Stream\ResponseBodyStream; use AsyncAws\Core\Stream\ResultStream; @@ -58,12 +60,12 @@ class PopulatorGenerator */ private $parserProvider; - public function __construct(ClassRegistry $classRegistry, NamespaceRegistry $namespaceRegistry, RequirementsRegistry $requirementsRegistry, ObjectGenerator $objectGenerator, array $managedMethods, ?TypeGenerator $typeGenerator = null, ?EnumGenerator $enumGenerator = null, ?ParserProvider $parserProvider = null) + public function __construct(ClassRegistry $classRegistry, NamespaceRegistry $namespaceRegistry, RequirementsRegistry $requirementsRegistry, ObjectGenerator $objectGenerator, ShapeUsageHelper $shapeUsageHelper, ?TypeGenerator $typeGenerator = null, ?EnumGenerator $enumGenerator = null, ?ParserProvider $parserProvider = null) { $this->objectGenerator = $objectGenerator; $this->requirementsRegistry = $requirementsRegistry; $this->typeGenerator = $typeGenerator ?? new TypeGenerator($namespaceRegistry); - $this->enumGenerator = $enumGenerator ?? new EnumGenerator($classRegistry, $namespaceRegistry, $managedMethods); + $this->enumGenerator = $enumGenerator ?? new EnumGenerator($classRegistry, $namespaceRegistry, $shapeUsageHelper); $this->parserProvider = $parserProvider ?? new ParserProvider($namespaceRegistry, $requirementsRegistry, $this->typeGenerator); } @@ -100,7 +102,7 @@ private function generateProperties(StructureShape $shape, ClassBuilder $classBu $this->enumGenerator->generate($memberShape); } - if ($memberShape instanceof StructureShape) { + if ($memberShape instanceof ObjectShape) { $this->objectGenerator->generate($memberShape); } elseif ($memberShape instanceof MapShape) { $mapKeyShape = $memberShape->getKey()->getShape(); @@ -111,7 +113,7 @@ private function generateProperties(StructureShape $shape, ClassBuilder $classBu $this->enumGenerator->generate($mapKeyShape); } - if (($valueShape = $memberShape->getValue()->getShape()) instanceof StructureShape) { + if (($valueShape = $memberShape->getValue()->getShape()) instanceof ObjectShape) { $this->objectGenerator->generate($valueShape); } if (!empty($valueShape->getEnum())) { @@ -300,7 +302,7 @@ private function generatePopulator(Operation $operation, StructureShape $shape, private function generateListShapeMemberShape(Shape $memberShape, bool $forEndpoint): void { - if ($memberShape instanceof StructureShape) { + if ($memberShape instanceof ObjectShape) { $this->objectGenerator->generate($memberShape, $forEndpoint); } elseif ($memberShape instanceof ListShape) { $this->generateListShapeMemberShape($memberShape->getMember()->getShape(), $forEndpoint); diff --git a/src/CodeGenerator/src/Generator/CodeGenerator/TypeGenerator.php b/src/CodeGenerator/src/Generator/CodeGenerator/TypeGenerator.php index da4e9bf4f..0ba357a33 100644 --- a/src/CodeGenerator/src/Generator/CodeGenerator/TypeGenerator.php +++ b/src/CodeGenerator/src/Generator/CodeGenerator/TypeGenerator.php @@ -7,8 +7,10 @@ use AsyncAws\CodeGenerator\Definition\DocumentShape; use AsyncAws\CodeGenerator\Definition\ListShape; use AsyncAws\CodeGenerator\Definition\MapShape; +use AsyncAws\CodeGenerator\Definition\ObjectShape; use AsyncAws\CodeGenerator\Definition\Shape; use AsyncAws\CodeGenerator\Definition\StructureShape; +use AsyncAws\CodeGenerator\Definition\UnionShape; use AsyncAws\CodeGenerator\Generator\Naming\ClassName; use AsyncAws\CodeGenerator\Generator\Naming\NamespaceRegistry; @@ -39,30 +41,116 @@ public function __construct(NamespaceRegistry $namespaceRegistry) * * @return array{string, ClassName[]} [docblock representation, ClassName related] */ - public function generateDocblock(StructureShape $shape, ClassName $shapeClassName, bool $alternateClass = true, bool $allNullable = false, bool $isObject = false, array $extra = []): array + public function generateDocblock(ObjectShape $shape, ClassName $shapeClassName, bool $alternateClass = true, bool $allNullable = false, bool $isObject = false, array $extra = []): array { - $classNames = []; + if ($shape instanceof StructureShape) { + [$definitions, $classNames] = $this->getDefinitionForStructure($shape, $allNullable, $isObject, $extra); + } elseif ($shape instanceof UnionShape) { + [$definitions, $classNames] = $this->getDefinitionForUnion($shape, $allNullable, $isObject, $extra); + } else { + throw new \RuntimeException('Unsupported shape type: ' . \get_class($shape)); + } + if ($alternateClass) { $classNames[] = $shapeClassName; + $definitions[] = $shapeClassName->getName(); + } + + $body = '@param ' . implode('|', $definitions) . ' $input'; + + return [$body, array_unique($classNames, \SORT_REGULAR)]; + } + + /** + * Return php type information for the given shape. + * + * @return array{string, string, ClassName[]} [typeHint value, docblock representation, ClassName related] + */ + public function getPhpType(Shape $shape): array + { + $memberClassNames = []; + if ($shape instanceof ObjectShape) { + $memberClassNames[] = $className = $this->namespaceRegistry->getObject($shape); + + return [$className->getFqdn(), $className->getName(), $memberClassNames]; + } + + if ($shape instanceof ListShape) { + $listMemberShape = $shape->getMember()->getShape(); + [$type, $doc, $memberClassNames] = $this->getPhpType($listMemberShape); + if (str_ends_with($doc, '::*')) { + $doc = "list<$doc>"; + } else { + $doc .= '[]'; + } + + return ['array', $doc, $memberClassNames]; } + + if ($shape instanceof MapShape) { + $mapKeyShape = $shape->getKey()->getShape(); + $mapValueShape = $shape->getValue()->getShape(); + [$type, $doc, $memberClassNames] = $this->getPhpType($mapValueShape); + if (!empty($mapKeyShape->getEnum())) { + $memberClassNames[] = $memberClassName = $this->namespaceRegistry->getEnum($mapKeyShape); + $doc = "array<{$memberClassName->getName()}::*, $doc>"; + } else { + $doc = "array"; + } + + return ['array', $doc, $memberClassNames]; + } + + if ($shape instanceof DocumentShape) { + return ['bool|string|int|float|array|null', 'bool|string|int|float|list|array|null', []]; + } + + $type = $doc = $this->getNativePhpType($shape->getType()); + if (!empty($shape->getEnum())) { + $memberClassNames[] = $memberClassName = $this->namespaceRegistry->getEnum($shape); + + $doc = $memberClassName->getName() . '::*'; + } + + return [$type, $doc, $memberClassNames]; + } + + private function getDefinitionForUnion(UnionShape $shape, bool $allNullable, bool $isObject, array $extra): array + { + $definitions = []; + $classNames = []; + foreach ($shape->getChildren() as $child) { + [$childDefinitions, $childClassNames] = $this->getDefinitionForStructure($child, $allNullable, $isObject, $extra); + $definitions = array_merge($definitions, $childDefinitions); + $classNames = array_merge($classNames, $childClassNames); + } + + $definitions = array_unique($definitions); + + return [$definitions, $classNames]; + } + + private function getDefinitionForStructure(StructureShape $shape, bool $allNullable, bool $isObject, array $extra): array + { + $classNames = []; if (empty($shape->getMembers()) && empty($extra)) { // No input array - return ['@param array' . ($alternateClass ? '|' . $shapeClassName->getName() : '') . ' $input', $classNames]; + return [['array'], $classNames]; } - $body = ['@param array{']; + $body = ['array{']; foreach ($shape->getMembers() as $member) { $nullable = !$member->isRequired(); $memberShape = $member->getShape(); - if ($memberShape instanceof StructureShape) { + if ($memberShape instanceof ObjectShape) { $classNames[] = $className = $this->namespaceRegistry->getObject($memberShape); $param = $className->getName() . '|array'; } elseif ($memberShape instanceof ListShape) { $listMemberShape = $memberShape->getMember()->getShape(); // is the list item an object? - if ($listMemberShape instanceof StructureShape) { + if ($listMemberShape instanceof ObjectShape) { $classNames[] = $className = $this->namespaceRegistry->getObject($listMemberShape); $param = 'array<' . $className->getName() . '|array>'; } elseif (!empty($listMemberShape->getEnum())) { @@ -75,7 +163,7 @@ public function generateDocblock(StructureShape $shape, ClassName $shapeClassNam $mapValueShape = $memberShape->getValue()->getShape(); // is the map item an object? - if ($mapValueShape instanceof StructureShape) { + if ($mapValueShape instanceof ObjectShape) { $classNames[] = $className = $this->namespaceRegistry->getObject($mapValueShape); $param = $className->getName() . '|array'; } elseif (!empty($mapValueShape->getEnum())) { @@ -125,64 +213,11 @@ public function generateDocblock(StructureShape $shape, ClassName $shapeClassNam $body[] = \sprintf(' %s: %s,', $phpdocMemberName, $param); } } - $body = array_merge($body, $extra); - $body[] = '}' . ($alternateClass ? '|' . $shapeClassName->getName() : '') . ' $input'; - return [implode("\n", $body), $classNames]; - } - - /** - * Return php type information for the given shape. - * - * @return array{string, string, ClassName[]} [typeHint value, docblock representation, ClassName related] - */ - public function getPhpType(Shape $shape): array - { - $memberClassNames = []; - if ($shape instanceof StructureShape) { - $memberClassNames[] = $className = $this->namespaceRegistry->getObject($shape); - - return [$className->getFqdn(), $className->getName(), $memberClassNames]; - } - - if ($shape instanceof ListShape) { - $listMemberShape = $shape->getMember()->getShape(); - [$type, $doc, $memberClassNames] = $this->getPhpType($listMemberShape); - if ('::*' === substr($doc, -3)) { - $doc = "list<$doc>"; - } else { - $doc .= '[]'; - } - - return ['array', $doc, $memberClassNames]; - } - - if ($shape instanceof MapShape) { - $mapKeyShape = $shape->getKey()->getShape(); - $mapValueShape = $shape->getValue()->getShape(); - [$type, $doc, $memberClassNames] = $this->getPhpType($mapValueShape); - if (!empty($mapKeyShape->getEnum())) { - $memberClassNames[] = $memberClassName = $this->namespaceRegistry->getEnum($mapKeyShape); - $doc = "array<{$memberClassName->getName()}::*, $doc>"; - } else { - $doc = "array"; - } - - return ['array', $doc, $memberClassNames]; - } - - if ($shape instanceof DocumentShape) { - return ['bool|string|int|float|array|null', 'bool|string|int|float|list|array|null', []]; - } - - $type = $doc = $this->getNativePhpType($shape->getType()); - if (!empty($shape->getEnum())) { - $memberClassNames[] = $memberClassName = $this->namespaceRegistry->getEnum($shape); - - $doc = $memberClassName->getName() . '::*'; - } + $body = array_merge($body, $extra); + $body[] = '}'; - return [$type, $doc, $memberClassNames]; + return [[implode("\n", $body)], $classNames]; } private function getNativePhpType(string $parameterType): string diff --git a/src/CodeGenerator/src/Generator/EnumGenerator.php b/src/CodeGenerator/src/Generator/EnumGenerator.php index b03f54b2b..a172ae3f0 100644 --- a/src/CodeGenerator/src/Generator/EnumGenerator.php +++ b/src/CodeGenerator/src/Generator/EnumGenerator.php @@ -4,10 +4,7 @@ namespace AsyncAws\CodeGenerator\Generator; -use AsyncAws\CodeGenerator\Definition\ListShape; -use AsyncAws\CodeGenerator\Definition\MapShape; use AsyncAws\CodeGenerator\Definition\Shape; -use AsyncAws\CodeGenerator\Definition\StructureShape; use AsyncAws\CodeGenerator\Generator\Naming\ClassName; use AsyncAws\CodeGenerator\Generator\Naming\NamespaceRegistry; use AsyncAws\CodeGenerator\Generator\PhpGenerator\ClassRegistry; @@ -35,25 +32,20 @@ class EnumGenerator private $namespaceRegistry; /** - * @var ClassName[] - */ - private $generated = []; - - /** - * @var list + * @var ShapeUsageHelper */ - private $managedMethods; + private $shapeUsageHelper; /** - * @var array|null + * @var ClassName[] */ - private $usedShapedOutput; + private $generated = []; - public function __construct(ClassRegistry $classRegistry, NamespaceRegistry $namespaceRegistry, array $managedMethods) + public function __construct(ClassRegistry $classRegistry, NamespaceRegistry $namespaceRegistry, ShapeUsageHelper $shapeUsageHelper) { $this->classRegistry = $classRegistry; $this->namespaceRegistry = $namespaceRegistry; - $this->managedMethods = $managedMethods; + $this->shapeUsageHelper = $shapeUsageHelper; } /** @@ -85,7 +77,7 @@ public function generate(Shape $shape): ClassName $availableConsts[] = 'self::' . $constName . ' => true'; } - if ($this->isShapeUsedOutput($shape)) { + if ($this->shapeUsageHelper->isShapeUsedOutput($shape)) { $classBuilder->addConstant(self::UNKNOWN_VALUE, self::UNKNOWN_VALUE)->setVisibility(Visibility::Public); } $classBuilder->addMethod('exists') @@ -125,49 +117,4 @@ public static function canonicalizeName(string $name): string return $name; } - - private function isShapeUsedOutput(Shape $shape): bool - { - if (null === $this->usedShapedOutput) { - $service = $shape->getService(); - $walk = function (?Shape $shape) use (&$walk) { - if (null === $shape) { - return; - } - if (isset($this->usedShapedOutput[$shape->getName()])) { - // Node already visited - return; - } - - $this->usedShapedOutput[$shape->getName()] = true; - if ($shape instanceof StructureShape) { - foreach ($shape->getMembers() as $member) { - $walk($member->getShape()); - } - } elseif ($shape instanceof ListShape) { - $walk($shape->getMember()->getShape()); - } elseif ($shape instanceof MapShape) { - $walk($shape->getKey()->getShape()); - $walk($shape->getValue()->getShape()); - } - }; - - foreach ($this->managedMethods as $method) { - if (null !== $operation = $service->getOperation($method)) { - $walk($operation->getOutput()); - foreach ($operation->getErrors() as $error) { - $walk($error); - } - } - if (null !== $waiter = $service->getWaiter($method)) { - $walk($waiter->getOperation()->getOutput()); - foreach ($waiter->getOperation()->getErrors() as $error) { - $walk($error); - } - } - } - } - - return $this->usedShapedOutput[$shape->getName()] ?? false; - } } diff --git a/src/CodeGenerator/src/Generator/InputGenerator.php b/src/CodeGenerator/src/Generator/InputGenerator.php index 7debad3c8..4affc387a 100644 --- a/src/CodeGenerator/src/Generator/InputGenerator.php +++ b/src/CodeGenerator/src/Generator/InputGenerator.php @@ -8,6 +8,7 @@ use AsyncAws\CodeGenerator\Definition\ListShape; use AsyncAws\CodeGenerator\Definition\MapShape; use AsyncAws\CodeGenerator\Definition\Member; +use AsyncAws\CodeGenerator\Definition\ObjectShape; use AsyncAws\CodeGenerator\Definition\Operation; use AsyncAws\CodeGenerator\Definition\StructureMember; use AsyncAws\CodeGenerator\Definition\StructureShape; @@ -79,13 +80,13 @@ class InputGenerator */ private $requirementsRegistry; - public function __construct(ClassRegistry $classRegistry, NamespaceRegistry $namespaceRegistry, RequirementsRegistry $requirementsRegistry, ObjectGenerator $objectGenerator, array $managedMethods, ?TypeGenerator $typeGenerator = null, ?EnumGenerator $enumGenerator = null, ?HookGenerator $hookGenerator = null) + public function __construct(ClassRegistry $classRegistry, NamespaceRegistry $namespaceRegistry, RequirementsRegistry $requirementsRegistry, ObjectGenerator $objectGenerator, ShapeUsageHelper $shapeUsageHelper, ?TypeGenerator $typeGenerator = null, ?EnumGenerator $enumGenerator = null, ?HookGenerator $hookGenerator = null) { $this->classRegistry = $classRegistry; $this->namespaceRegistry = $namespaceRegistry; $this->objectGenerator = $objectGenerator; $this->typeGenerator = $typeGenerator ?? new TypeGenerator($this->namespaceRegistry); - $this->enumGenerator = $enumGenerator ?? new EnumGenerator($this->classRegistry, $this->namespaceRegistry, $managedMethods); + $this->enumGenerator = $enumGenerator ?? new EnumGenerator($this->classRegistry, $this->namespaceRegistry, $shapeUsageHelper); $this->hookGenerator = $hookGenerator ?? new HookGenerator(); $this->serializer = new SerializerProvider($this->namespaceRegistry, $requirementsRegistry); $this->requirementsRegistry = $requirementsRegistry; @@ -123,7 +124,7 @@ public function generate(Operation $operation): ClassName $getterSetterNullable = true; $typeAlreadyNullable = false; - if ($memberShape instanceof StructureShape) { + if ($memberShape instanceof ObjectShape) { $memberClassName = $this->objectGenerator->generate($memberShape); $constructorBody .= strtr('$this->PROPERTY = isset($input["NAME"]) ? CLASS::create($input["NAME"]) : null;' . "\n", ['PROPERTY' => GeneratorHelper::normalizeName($member->getName()), 'NAME' => $member->getName(), 'CLASS' => $memberClassName->getName()]); } elseif ($memberShape instanceof ListShape) { @@ -133,18 +134,16 @@ public function generate(Operation $operation): ClassName } $getterSetterNullable = false; - if ($listMemberShape instanceof StructureShape) { - $getterSetterNullable = false; + if ($listMemberShape instanceof ObjectShape) { $memberClassName = $this->objectGenerator->generate($listMemberShape); $constructorBody .= strtr('$this->PROPERTY = isset($input["NAME"]) ? array_map([CLASS::class, "create"], $input["NAME"]) : null;' . "\n", ['PROPERTY' => GeneratorHelper::normalizeName($member->getName()), 'NAME' => $member->getName(), 'CLASS' => $memberClassName->getName()]); } elseif ($listMemberShape instanceof ListShape) { - $getterSetterNullable = false; $listMemberShapelevel2 = $listMemberShape->getMember()->getShape(); if (!empty($listMemberShapelevel2->getEnum())) { $this->enumGenerator->generate($listMemberShapelevel2); } - if ($listMemberShapelevel2 instanceof StructureShape) { + if ($listMemberShapelevel2 instanceof ObjectShape) { $memberClassName = $this->objectGenerator->generate($listMemberShapelevel2); $constructorBody .= strtr('$this->PROPERTY = isset($input["NAME"]) ? array_map(static function(array $array) { return array_map([CLASS::class, "create"], $array); @@ -171,7 +170,7 @@ public function generate(Operation $operation): ClassName $getterSetterNullable = false; // Is this a list of objects? - if ($mapValueShape instanceof StructureShape) { + if ($mapValueShape instanceof ObjectShape) { $memberClassName = $this->objectGenerator->generate($mapValueShape); $constructorBody .= strtr(' @@ -189,7 +188,7 @@ public function generate(Operation $operation): ClassName } elseif ($mapValueShape instanceof ListShape) { $listMember = $mapValueShape->getMember(); $listMemberShape = $listMember->getShape(); - if (!$listMemberShape instanceof StructureShape) { + if (!$listMemberShape instanceof ObjectShape) { throw new \RuntimeException('Recursive ListShape with non StructureShape member is not implemented.'); } $memberClassName = $this->objectGenerator->generate($listMemberShape); @@ -509,8 +508,8 @@ private function inputClassRequestGetters(StructureShape $inputShape, ClassBuild if ('' === trim($serializerBuilderResult->getBody())) { $body['body'] = '$body = "";'; } else { - $method = $classBuilder->addMethod('requestBody')->setReturnType($serializerBuilderResult->getReturnType())->setBody($serializerBuilderResult->getBody())->setPrivate(); - foreach ($serializerBodyResult->getExtraMethodArgs() + $serializerBuilderResult->getExtraMethodArgs() as $arg => $type) { + $method = $classBuilder->addMethod('requestBody')->setReturnType($serializer->getRequestBuilderReturnType())->setBody($serializerBuilderResult->getBody())->setPrivate(); + foreach ($serializerBodyResult->getExtraMethodArgs() + $serializer->getRequestBuilderExtraArguments() as $arg => $type) { $method->addParameter($arg)->setType($type); } } @@ -623,14 +622,14 @@ private function addUse(StructureShape $shape, ClassBuilder $classBuilder, array $classBuilder->addUse($this->namespaceRegistry->getEnum($memberShape)->getFqdn()); } - if ($memberShape instanceof StructureShape) { + if ($memberShape instanceof ObjectShape) { $fqdn = $this->namespaceRegistry->getObject($memberShape)->getFqdn(); if (!\in_array($fqdn, $addedFqdn)) { $addedFqdn[] = $fqdn; $classBuilder->addUse($fqdn); } } elseif ($memberShape instanceof MapShape) { - if (($valueShape = $memberShape->getValue()->getShape()) instanceof StructureShape) { + if (($valueShape = $memberShape->getValue()->getShape()) instanceof ObjectShape) { $fqdn = $this->namespaceRegistry->getObject($valueShape)->getFqdn(); if (!\in_array($fqdn, $addedFqdn)) { $addedFqdn[] = $fqdn; @@ -641,7 +640,7 @@ private function addUse(StructureShape $shape, ClassBuilder $classBuilder, array $classBuilder->addUse($this->namespaceRegistry->getEnum($valueShape)->getFqdn()); } } elseif ($memberShape instanceof ListShape) { - if (($memberShape = $memberShape->getMember()->getShape()) instanceof StructureShape) { + if (($memberShape = $memberShape->getMember()->getShape()) instanceof ObjectShape) { $fqdn = $this->namespaceRegistry->getObject($memberShape)->getFqdn(); if (!\in_array($fqdn, $addedFqdn)) { $addedFqdn[] = $fqdn; diff --git a/src/CodeGenerator/src/Generator/ObjectGenerator.php b/src/CodeGenerator/src/Generator/ObjectGenerator.php index 917b0753b..97dfe8022 100644 --- a/src/CodeGenerator/src/Generator/ObjectGenerator.php +++ b/src/CodeGenerator/src/Generator/ObjectGenerator.php @@ -7,8 +7,9 @@ use AsyncAws\CodeGenerator\Definition\DocumentShape; use AsyncAws\CodeGenerator\Definition\ListShape; use AsyncAws\CodeGenerator\Definition\MapShape; -use AsyncAws\CodeGenerator\Definition\Shape; +use AsyncAws\CodeGenerator\Definition\ObjectShape; use AsyncAws\CodeGenerator\Definition\StructureShape; +use AsyncAws\CodeGenerator\Definition\UnionShape; use AsyncAws\CodeGenerator\Generator\CodeGenerator\TypeGenerator; use AsyncAws\CodeGenerator\Generator\Composer\RequirementsRegistry; use AsyncAws\CodeGenerator\Generator\Naming\ClassName; @@ -18,6 +19,7 @@ use AsyncAws\CodeGenerator\Generator\RequestSerializer\SerializerProvider; use AsyncAws\Core\EndpointDiscovery\EndpointInterface; use AsyncAws\Core\Exception\InvalidArgument; +use AsyncAws\Core\Exception\LogicException; use AsyncAws\Core\Stream\ResultStream; /** @@ -61,33 +63,43 @@ class ObjectGenerator private $serializer; /** - * @var list + * @var ShapeUsageHelper */ - private $managedMethods; + private $shapeUsageHelper; - /** - * @var array|null - */ - private $usedShapedInput; - - /** - * @param list $managedMethods - */ - public function __construct(ClassRegistry $classRegistry, NamespaceRegistry $namespaceRegistry, RequirementsRegistry $requirementsRegistry, array $managedMethods, ?TypeGenerator $typeGenerator = null, ?EnumGenerator $enumGenerator = null) + public function __construct(ClassRegistry $classRegistry, NamespaceRegistry $namespaceRegistry, RequirementsRegistry $requirementsRegistry, ShapeUsageHelper $shapeUsageHelper, ?TypeGenerator $typeGenerator = null, ?EnumGenerator $enumGenerator = null) { $this->classRegistry = $classRegistry; $this->namespaceRegistry = $namespaceRegistry; $this->typeGenerator = $typeGenerator ?? new TypeGenerator($this->namespaceRegistry); - $this->enumGenerator = $enumGenerator ?? new EnumGenerator($this->classRegistry, $this->namespaceRegistry, $managedMethods); + $this->enumGenerator = $enumGenerator ?? new EnumGenerator($this->classRegistry, $this->namespaceRegistry, $shapeUsageHelper); $this->serializer = new SerializerProvider($this->namespaceRegistry, $requirementsRegistry); - $this->managedMethods = $managedMethods; + $this->shapeUsageHelper = $shapeUsageHelper; } - public function generate(StructureShape $shape, bool $forEndpoint = false): ClassName + public function generate(ObjectShape $shape, bool $forEndpoint = false): ClassName { if (isset($this->generated[$shape->getName()])) { return $this->generated[$shape->getName()]; } + + if ($shape instanceof StructureShape) { + return $this->generateStructure($shape, $forEndpoint); + } + + if ($shape instanceof UnionShape) { + if ($forEndpoint) { + throw new InvalidArgument('Union shapes are not supported for endpoint discovery.'); + } + + return $this->generateUnion($shape); + } + + throw new InvalidArgument('Unsupported object shape: ' . \get_class($shape)); + } + + private function generateStructure(StructureShape $shape, bool $forEndpoint = false): ClassName + { $this->generated[$shape->getName()] = $className = $this->namespaceRegistry->getObject($shape); $classBuilder = $this->classRegistry->register($className->getFqdn()); @@ -97,7 +109,8 @@ public function generate(StructureShape $shape, bool $forEndpoint = false): Clas } // Named constructor - $this->namedConstructor($shape, $classBuilder); + $this->generateConstructor($shape, $classBuilder); + $this->generateNamedConstructor($shape, $classBuilder); $this->addProperties($shape, $classBuilder, $forEndpoint); if ($forEndpoint) { @@ -106,14 +119,14 @@ public function generate(StructureShape $shape, bool $forEndpoint = false): Clas } $serializer = $this->serializer->get($shape->getService()); - if ($this->isShapeUsedInput($shape)) { + if ($this->shapeUsageHelper->isShapeUsedInput($shape)) { $serializerBuilderResult = $serializer->generateRequestBuilder($shape, false); foreach ($serializerBuilderResult->getUsedClasses() as $classNameFqdn) { $classBuilder->addUse($classNameFqdn); } - $method = $classBuilder->addMethod('requestBody')->setReturnType($serializerBuilderResult->getReturnType())->setBody($serializerBuilderResult->getBody())->setPublic()->setComment('@internal'); - foreach ($serializerBuilderResult->getExtraMethodArgs() as $arg => $type) { + $method = $classBuilder->addMethod('requestBody')->setReturnType($serializer->getRequestBuilderReturnType())->setBody($serializerBuilderResult->getBody())->setPublic()->setComment('@internal'); + foreach ($serializer->getRequestBuilderExtraArguments() as $arg => $type) { $method->addParameter($arg)->setType($type); } } @@ -121,43 +134,100 @@ public function generate(StructureShape $shape, bool $forEndpoint = false): Clas return $className; } - private function isShapeUsedInput(StructureShape $shape): bool + private function generateUnion(UnionShape $shape): ClassName { - if (null === $this->usedShapedInput) { - $service = $shape->getService(); - $walk = function (Shape $shape) use (&$walk) { - if (isset($this->usedShapedInput[$shape->getName()])) { - // Node already visited - return; - } + $this->generated[$shape->getName()] = $abstractClassName = $this->namespaceRegistry->getObject($shape); + + // first generates the Abstract class + $classBuilder = $classBuilderForAbstract = $this->classRegistry->register($abstractClassName->getFqdn()); + $classBuilder->setAbstract(); + if (null !== $documentation = $shape->getDocumentationMain()) { + $classBuilder->addComment(GeneratorHelper::parseDocumentation($documentation)); + } + + $this->generateNamedConstructorForUnion($shape, $classBuilder); + + if ($this->shapeUsageHelper->isShapeUsedInput($shape)) { + $serializer = $this->serializer->get($shape->getService()); + + $method = $classBuilder->addMethod('requestBody') + ->setReturnType($serializer->getRequestBuilderReturnType()) + ->setAbstract() + ->setPublic() + ->setComment('@internal'); + foreach ($serializer->getRequestBuilderExtraArguments() as $arg => $type) { + $method->addParameter($arg)->setType($type); + } + } - $this->usedShapedInput[$shape->getName()] = true; - if ($shape instanceof StructureShape) { - foreach ($shape->getMembers() as $member) { - $walk($member->getShape()); - } - } elseif ($shape instanceof ListShape) { - $walk($shape->getMember()->getShape()); - } elseif ($shape instanceof MapShape) { - $walk($shape->getKey()->getShape()); - $walk($shape->getValue()->getShape()); + $serializer = $this->serializer->get($shape->getService()); + // generate one class per member + $inheritors = []; + foreach ($shape->getChildren() as $child) { + $this->generated[$child->getName()] = $childClassName = $this->namespaceRegistry->getObject($child); + $classBuilder = $this->classRegistry->register($childClassName->getFqdn()); + $classBuilder->setFinal()->setExtends($abstractClassName->getFqdn()); + $inheritors[] = $childClassName->getName(); + + $this->generateConstructor($child, $classBuilder); + $this->addProperties($child, $classBuilder, false); + + if ($this->shapeUsageHelper->isShapeUsedInput($shape)) { + $serializerBuilderResult = $serializer->generateRequestBuilder($child, false); + foreach ($serializerBuilderResult->getUsedClasses() as $classNameFqdn) { + $classBuilder->addUse($classNameFqdn); } - }; - foreach ($this->managedMethods as $method) { - if (null !== $operation = $service->getOperation($method)) { - $walk($operation->getInput()); + $method = $classBuilder->addMethod('requestBody')->setReturnType($serializer->getRequestBuilderReturnType())->setBody($serializerBuilderResult->getBody())->setPublic()->setComment('@internal'); + foreach ($serializer->getRequestBuilderExtraArguments() as $arg => $type) { + $method->addParameter($arg)->setType($type); } - if (null !== $waiter = $service->getWaiter($method)) { - $walk($waiter->getOperation()->getInput()); + } + } + + if ($this->shapeUsageHelper->isShapeUsedOutput($shape)) { + // generate fallback for unknown member + $child = $shape->getChildForUnknown(); + $this->generated[$child->getName()] = $childClassName = $this->namespaceRegistry->getObject($child); + $classBuilder = $this->classRegistry->register($childClassName->getFqdn()); + $classBuilder->setFinal()->setExtends($abstractClassName->getFqdn()); + $inheritors[] = $childClassName->getName(); + + $classBuilder->addMethod('__construct') + ->setComment('@internal') + ->addComment('@param array $input') + ->setBody('$this->unknown = $input;') + ->addParameter('input')->setType('array') + ; + + $classBuilder->addProperty('unknown') + ->setPrivate() + ->addComment('@var array'); + + $classBuilder->addMethod('getUnknown') + ->setReturnType('array') + ->addComment('@return array') + ->setBody('return $this->unknown;'); + + if ($this->shapeUsageHelper->isShapeUsedInput($shape)) { + $method = $classBuilder->addMethod('requestBody') + ->setPublic() + ->setReturnType($serializer->getRequestBuilderReturnType()) + ->setComment('@internal') + ->setBody('throw new LogicException(\'request can not be generated for unknown object\');'); + $classBuilder->addUse(LogicException::class); + foreach ($serializer->getRequestBuilderExtraArguments() as $arg => $type) { + $method->addParameter($arg)->setType($type); } } } - return $this->usedShapedInput[$shape->getName()] ?? false; + $classBuilderForAbstract->addComment('@psalm-inheritors ' . implode('|', $inheritors)); + + return $abstractClassName; } - private function namedConstructor(StructureShape $shape, ClassBuilder $classBuilder): void + private function generateNamedConstructor(StructureShape $shape, ClassBuilder $classBuilder): void { if (empty($shape->getMembers())) { $createMethod = $classBuilder->addMethod('create') @@ -184,8 +254,44 @@ private function namedConstructor(StructureShape $shape, ClassBuilder $classBuil foreach ($memberClassNames as $memberClassName) { $classBuilder->addUse($memberClassName->getFqdn()); } + } + + private function generateNamedConstructorForUnion(UnionShape $shape, ClassBuilder $classBuilder): void + { + $body = ['if ($input instanceof self) { + return $input; + }']; + /** @var StructureShape $children */ + foreach ($shape->getChildren() as $name => $children) { + $memberClassName = $this->namespaceRegistry->getObject($children); + $classBuilder->addUse($memberClassName->getFqdn()); + $body[] = strtr('if (isset($input[NAME])) { + return new CLASS([NAME => $input[NAME]]); + }', [ + 'NAME' => var_export($name, true), + 'CLASS' => $memberClassName->getName(), + ]); + } + $classBuilder->addUse(InvalidArgument::class); + $body[] = 'throw new InvalidArgument(\'Invalid union input\');'; + + $createMethod = $classBuilder->addMethod('create') + ->setStatic(true) + ->setReturnType('self') + ->setBody(implode("\n", $body)); + $createMethod->addParameter('input'); + [$doc, $memberClassNames] = $this->typeGenerator->generateDocblock($shape, $this->generated[$shape->getName()], true, false, true); + $createMethod->addComment($doc); + foreach ($memberClassNames as $memberClassName) { + $classBuilder->addUse($memberClassName->getFqdn()); + } + } - // We need a constructor + private function generateConstructor(StructureShape $shape, ClassBuilder $classBuilder): void + { + if (empty($shape->getMembers())) { + return; + } $constructor = $classBuilder->addMethod('__construct'); [$doc, $memberClassNames] = $this->typeGenerator->generateDocblock($shape, $this->generated[$shape->getName()], false, false, true); $constructor->addComment($doc); @@ -201,14 +307,14 @@ private function namedConstructor(StructureShape $shape, ClassBuilder $classBuil $constructorBody = ''; foreach ($shape->getMembers() as $member) { $memberShape = $member->getShape(); - if ($memberShape instanceof StructureShape) { + if ($memberShape instanceof ObjectShape) { $objectClass = $this->generate($memberShape); $memberCode = strtr('CLASS::create($input["NAME"])', ['NAME' => $member->getName(), 'CLASS' => $objectClass->getName()]); } elseif ($memberShape instanceof ListShape) { $listMemberShape = $memberShape->getMember()->getShape(); // Check if this is a list of objects - if ($listMemberShape instanceof StructureShape) { + if ($listMemberShape instanceof ObjectShape) { $objectClass = $this->generate($listMemberShape); $memberCode = strtr('array_map([CLASS::class, "create"], $input["NAME"])', ['NAME' => $member->getName(), 'CLASS' => $objectClass->getName()]); } else { @@ -217,7 +323,7 @@ private function namedConstructor(StructureShape $shape, ClassBuilder $classBuil } elseif ($memberShape instanceof MapShape) { $mapValueShape = $memberShape->getValue()->getShape(); - if ($mapValueShape instanceof StructureShape) { + if ($mapValueShape instanceof ObjectShape) { $objectClass = $this->generate($mapValueShape); $memberCode = strtr('array_map([CLASS::class, "create"], $input["NAME"])', ['NAME' => $member->getName(), 'CLASS' => $objectClass->getName()]); } else { @@ -280,7 +386,7 @@ private function addProperties(StructureShape $shape, ClassBuilder $classBuilder $getterSetterNullable = null; $typeAlreadyNullable = false; - if ($memberShape instanceof StructureShape) { + if ($memberShape instanceof ObjectShape) { $this->generate($memberShape); } elseif ($memberShape instanceof MapShape) { $getterSetterNullable = false; @@ -292,7 +398,7 @@ private function addProperties(StructureShape $shape, ClassBuilder $classBuilder $this->enumGenerator->generate($mapKeyShape); } - if (($valueShape = $memberShape->getValue()->getShape()) instanceof StructureShape) { + if (($valueShape = $memberShape->getValue()->getShape()) instanceof ObjectShape) { $this->generate($valueShape); } if (!empty($valueShape->getEnum())) { @@ -303,7 +409,7 @@ private function addProperties(StructureShape $shape, ClassBuilder $classBuilder $getterSetterNullable = false; $memberShape->getMember()->getShape(); - if (($memberShape = $memberShape->getMember()->getShape()) instanceof StructureShape) { + if (($memberShape = $memberShape->getMember()->getShape()) instanceof ObjectShape) { $this->generate($memberShape); } if (!empty($memberShape->getEnum())) { diff --git a/src/CodeGenerator/src/Generator/PhpGenerator/ClassBuilder.php b/src/CodeGenerator/src/Generator/PhpGenerator/ClassBuilder.php index 9347ea3f2..c8f04ef02 100644 --- a/src/CodeGenerator/src/Generator/PhpGenerator/ClassBuilder.php +++ b/src/CodeGenerator/src/Generator/PhpGenerator/ClassBuilder.php @@ -83,6 +83,14 @@ public function setFinal(bool $state = true): self return $this; } + public function setAbstract(bool $state = true): self + { + $this->setFinal(false); + $this->class->setAbstract($state); + + return $this; + } + /** * @param mixed $value */ diff --git a/src/CodeGenerator/src/Generator/RequestSerializer/QuerySerializer.php b/src/CodeGenerator/src/Generator/RequestSerializer/QuerySerializer.php index f593800d2..4b77211f0 100644 --- a/src/CodeGenerator/src/Generator/RequestSerializer/QuerySerializer.php +++ b/src/CodeGenerator/src/Generator/RequestSerializer/QuerySerializer.php @@ -8,6 +8,7 @@ use AsyncAws\CodeGenerator\Definition\ListShape; use AsyncAws\CodeGenerator\Definition\MapShape; use AsyncAws\CodeGenerator\Definition\Member; +use AsyncAws\CodeGenerator\Definition\ObjectShape; use AsyncAws\CodeGenerator\Definition\Operation; use AsyncAws\CodeGenerator\Definition\Shape; use AsyncAws\CodeGenerator\Definition\StructureMember; @@ -79,7 +80,7 @@ public function generateRequestBody(Operation $operation, StructureShape $shape) ]), true, $this->usedClassesFlush()); } - public function generateRequestBuilder(StructureShape $shape, bool $needsChecks): SerializerResultBuilder + public function generateRequestBuilder(StructureShape $shape, bool $needsChecks): SerializerResult { $this->usedClassesInit(); $body = implode("\n", array_map(function (StructureMember $member) use ($needsChecks) { @@ -125,7 +126,7 @@ public function generateRequestBuilder(StructureShape $shape, bool $needsChecks) ]); }, $shape->getMembers())); - return new SerializerResultBuilder('array', strtr(' + return new SerializerResult(strtr(' $payload = []; CHILDREN_CODE @@ -135,6 +136,16 @@ public function generateRequestBuilder(StructureShape $shape, bool $needsChecks) ]), $this->usedClassesFlush()); } + public function getRequestBuilderReturnType(): string + { + return 'array'; + } + + public function getRequestBuilderExtraArguments(): array + { + return []; + } + private function getQueryName(Member $member, string $default): string { if (null !== $member->getQueryName()) { @@ -178,8 +189,8 @@ private function getName(Member $member): string private function dumpArrayElement(string $output, string $input, string $contextProperty, Shape $shape): string { switch (true) { - case $shape instanceof StructureShape: - return $this->dumpArrayStructure($output, $input, $shape); + case $shape instanceof ObjectShape: + return $this->dumpArrayStructure($output, $input); case $shape instanceof ListShape: return $this->dumpArrayList($output, $input, $contextProperty, $shape); case $shape instanceof MapShape: @@ -204,7 +215,7 @@ private function dumpArrayElement(string $output, string $input, string $context throw new \RuntimeException(\sprintf('Type %s is not yet implemented', $shape->getType())); } - private function dumpArrayStructure(string $output, string $input, StructureShape $shape): string + private function dumpArrayStructure(string $output, string $input): string { return strtr('foreach (INPUT->requestBody() as $bodyKey => $bodyValue) { $payload["OUTPUT.$bodyKey"] = $bodyValue; diff --git a/src/CodeGenerator/src/Generator/RequestSerializer/RestJsonSerializer.php b/src/CodeGenerator/src/Generator/RequestSerializer/RestJsonSerializer.php index e98ae8ef7..be926be5f 100644 --- a/src/CodeGenerator/src/Generator/RequestSerializer/RestJsonSerializer.php +++ b/src/CodeGenerator/src/Generator/RequestSerializer/RestJsonSerializer.php @@ -8,6 +8,7 @@ use AsyncAws\CodeGenerator\Definition\ListShape; use AsyncAws\CodeGenerator\Definition\MapShape; use AsyncAws\CodeGenerator\Definition\Member; +use AsyncAws\CodeGenerator\Definition\ObjectShape; use AsyncAws\CodeGenerator\Definition\Operation; use AsyncAws\CodeGenerator\Definition\Shape; use AsyncAws\CodeGenerator\Definition\StructureMember; @@ -81,7 +82,7 @@ public function generateRequestBody(Operation $operation, StructureShape $shape) return new SerializerResultBody('$bodyPayload = $this->requestBody(); $body = empty($bodyPayload) ? "{}" : \json_encode($bodyPayload, ' . \JSON_THROW_ON_ERROR . ');', true, $this->usedClassesFlush()); } - public function generateRequestBuilder(StructureShape $shape, bool $needsChecks): SerializerResultBuilder + public function generateRequestBuilder(StructureShape $shape, bool $needsChecks): SerializerResult { $this->usedClassesInit(); $body = implode("\n", array_map(function (StructureMember $member) use ($needsChecks) { @@ -127,7 +128,7 @@ public function generateRequestBuilder(StructureShape $shape, bool $needsChecks) ]); }, $shape->getMembers())); - return new SerializerResultBuilder('array', strtr(' + return new SerializerResult(strtr(' $payload = []; CHILDREN_CODE @@ -137,6 +138,16 @@ public function generateRequestBuilder(StructureShape $shape, bool $needsChecks) ]), $this->usedClassesFlush()); } + public function getRequestBuilderReturnType(): string + { + return 'array'; + } + + public function getRequestBuilderExtraArguments(): array + { + return []; + } + protected function dumpArrayBoolean(string $output, string $input, Shape $shape): string { return strtr('$payloadOUTPUT = (bool) INPUT;', [ @@ -175,8 +186,8 @@ private function getName(Member $member): string private function dumpArrayElement(string $output, string $input, string $contextProperty, Shape $shape, bool $isRequired = false): string { switch (true) { - case $shape instanceof StructureShape: - return $this->dumpArrayStructure($output, $input, $shape); + case $shape instanceof ObjectShape: + return $this->dumpArrayStructure($output, $input); case $shape instanceof ListShape: return $this->dumpArrayList($output, $input, $contextProperty, $shape); case $shape instanceof MapShape: @@ -203,7 +214,7 @@ private function dumpArrayElement(string $output, string $input, string $context throw new \RuntimeException(\sprintf('Type %s is not yet implemented', $shape->getType())); } - private function dumpArrayStructure(string $output, string $input, StructureShape $shape): string + private function dumpArrayStructure(string $output, string $input): string { return strtr('$payloadOUTPUT = INPUT->requestBody();', [ 'OUTPUT' => $output, diff --git a/src/CodeGenerator/src/Generator/RequestSerializer/RestXmlSerializer.php b/src/CodeGenerator/src/Generator/RequestSerializer/RestXmlSerializer.php index 2c17f7d56..594c7d922 100644 --- a/src/CodeGenerator/src/Generator/RequestSerializer/RestXmlSerializer.php +++ b/src/CodeGenerator/src/Generator/RequestSerializer/RestXmlSerializer.php @@ -103,7 +103,7 @@ public function generateRequestBody(Operation $operation, StructureShape $shape) ', true, $this->usedClassesFlush(), ['node' => \DOMNode::class]); } - public function generateRequestBuilder(StructureShape $shape, bool $needsChecks): SerializerResultBuilder + public function generateRequestBuilder(StructureShape $shape, bool $needsChecks): SerializerResult { $this->usedClassesInit(); $body = implode("\n", array_map(function (StructureMember $member) use ($needsChecks) { @@ -149,7 +149,17 @@ public function generateRequestBuilder(StructureShape $shape, bool $needsChecks) ]); }, $shape->getMembers())); - return new SerializerResultBuilder('void', $body, $this->usedClassesFlush(), ['node' => \DOMElement::class, 'document' => \DOMDocument::class]); + return new SerializerResult($body, $this->usedClassesFlush()); + } + + public function getRequestBuilderReturnType(): string + { + return 'void'; + } + + public function getRequestBuilderExtraArguments(): array + { + return ['node' => \DOMElement::class, 'document' => \DOMDocument::class]; } private function dumpXmlShape(Member $member, Shape $shape, string $output, string $input): string diff --git a/src/CodeGenerator/src/Generator/RequestSerializer/Serializer.php b/src/CodeGenerator/src/Generator/RequestSerializer/Serializer.php index 784ff84ab..e5b41dc0e 100644 --- a/src/CodeGenerator/src/Generator/RequestSerializer/Serializer.php +++ b/src/CodeGenerator/src/Generator/RequestSerializer/Serializer.php @@ -25,7 +25,11 @@ public function generateRequestBody(Operation $operation, StructureShape $shape) /** * Returns the return type, the body and extra arguments for the requestBody method. */ - public function generateRequestBuilder(StructureShape $shape, bool $needsChecks): SerializerResultBuilder; + public function generateRequestBuilder(StructureShape $shape, bool $needsChecks): SerializerResult; + + public function getRequestBuilderReturnType(): string; + + public function getRequestBuilderExtraArguments(): array; public function getHeaders(Operation $operation, bool $withPayload): string; } diff --git a/src/CodeGenerator/src/Generator/RequestSerializer/SerializerResult.php b/src/CodeGenerator/src/Generator/RequestSerializer/SerializerResult.php index fcb12be7b..075237149 100644 --- a/src/CodeGenerator/src/Generator/RequestSerializer/SerializerResult.php +++ b/src/CodeGenerator/src/Generator/RequestSerializer/SerializerResult.php @@ -22,19 +22,12 @@ class SerializerResult private $usedClasses; /** - * @var array + * @param list $usedClasses */ - private $extraMethodArgs; - - /** - * @param list $usedClasses - * @param array $extraMethodArgs - */ - public function __construct(string $body, array $usedClasses = [], array $extraMethodArgs = []) + public function __construct(string $body, array $usedClasses = []) { $this->body = $body; $this->usedClasses = $usedClasses; - $this->extraMethodArgs = $extraMethodArgs; } public function getBody(): string @@ -49,12 +42,4 @@ public function getUsedClasses(): array { return $this->usedClasses; } - - /** - * @return array - */ - public function getExtraMethodArgs(): array - { - return $this->extraMethodArgs; - } } diff --git a/src/CodeGenerator/src/Generator/RequestSerializer/SerializerResultBody.php b/src/CodeGenerator/src/Generator/RequestSerializer/SerializerResultBody.php index e6ee19309..e132cc542 100644 --- a/src/CodeGenerator/src/Generator/RequestSerializer/SerializerResultBody.php +++ b/src/CodeGenerator/src/Generator/RequestSerializer/SerializerResultBody.php @@ -14,14 +14,28 @@ class SerializerResultBody extends SerializerResult */ private $requestBody; + /** + * @var array + */ + private $extraMethodArgs; + public function __construct(string $body, bool $requestBody, array $usedClasses = [], array $extraMethodArgs = []) { - parent::__construct($body, $usedClasses, $extraMethodArgs); + parent::__construct($body, $usedClasses); $this->requestBody = $requestBody; + $this->extraMethodArgs = $extraMethodArgs; } public function hasRequestBody(): bool { return $this->requestBody; } + + /** + * @return array + */ + public function getExtraMethodArgs(): array + { + return $this->extraMethodArgs; + } } diff --git a/src/CodeGenerator/src/Generator/RequestSerializer/SerializerResultBuilder.php b/src/CodeGenerator/src/Generator/RequestSerializer/SerializerResultBuilder.php deleted file mode 100644 index 1a491fd47..000000000 --- a/src/CodeGenerator/src/Generator/RequestSerializer/SerializerResultBuilder.php +++ /dev/null @@ -1,27 +0,0 @@ -returnType = $returnType; - } - - public function getReturnType(): string - { - return $this->returnType; - } -} diff --git a/src/CodeGenerator/src/Generator/ResponseParser/RestJsonParser.php b/src/CodeGenerator/src/Generator/ResponseParser/RestJsonParser.php index e095514a7..26471c770 100644 --- a/src/CodeGenerator/src/Generator/ResponseParser/RestJsonParser.php +++ b/src/CodeGenerator/src/Generator/ResponseParser/RestJsonParser.php @@ -8,9 +8,11 @@ use AsyncAws\CodeGenerator\Definition\ListShape; use AsyncAws\CodeGenerator\Definition\MapShape; use AsyncAws\CodeGenerator\Definition\Member; +use AsyncAws\CodeGenerator\Definition\ObjectShape; use AsyncAws\CodeGenerator\Definition\Shape; use AsyncAws\CodeGenerator\Definition\StructureMember; use AsyncAws\CodeGenerator\Definition\StructureShape; +use AsyncAws\CodeGenerator\Definition\UnionShape; use AsyncAws\CodeGenerator\Generator\CodeGenerator\TypeGenerator; use AsyncAws\CodeGenerator\Generator\Composer\RequirementsRegistry; use AsyncAws\CodeGenerator\Generator\EnumGenerator; @@ -195,6 +197,8 @@ private function parseElement(string $input, Shape $shape, bool $required, bool return $this->parseResponseList($shape, $input, $required, $inObject); case $shape instanceof StructureShape: return $this->parseResponseStructure($shape, $input, $required); + case $shape instanceof UnionShape: + return $this->parseResponseUnion($shape, $input, $required); case $shape instanceof MapShape: return $this->parseResponseMap($shape, $input, $required, $inObject); case $shape instanceof DocumentShape: @@ -225,6 +229,54 @@ private function parseElement(string $input, Shape $shape, bool $required, bool throw new \RuntimeException(\sprintf('Type %s is not yet implemented', $shape->getType())); } + private function parseResponseUnion(UnionShape $shape, string $input, bool $required): string + { + $functionName = 'populateResult' . ucfirst($shape->getName()); + if (!isset($this->generatedFunctions[$functionName])) { + // prevent recursion + $this->generatedFunctions[$functionName] = true; + + $body = []; + foreach ($shape->getChildren() as $name => $child) { + $body[] = strtr('if (isset($json[PROPERTY_NAME])) { + return PROPERTY_ACCESSOR; + }', [ + 'PROPERTY_NAME' => var_export($name, true), + 'PROPERTY_ACCESSOR' => $this->parseElement('$json', $child, true, true), + ]); + $className = $this->namespaceRegistry->getObject($child); + $this->imports[] = $className; + } + + // Generate fallback for unknown member + $childForUnknown = $shape->getChildForUnknown(); + $functionNameForUnknown = 'populateResult' . ucfirst($childForUnknown->getName()); + $className = $this->namespaceRegistry->getObject($childForUnknown); + $this->imports[] = $className; + $this->functions[$functionNameForUnknown] = $this->createPopulateMethod($functionNameForUnknown, strtr('return new CLASS_NAME($json);', [ + 'INPUT' => $input, + 'CLASS_NAME' => $className->getName(), + ]), $shape); + + $body[] = strtr('return $this->FUNCTION_NAME($json);', [ + 'FUNCTION_NAME' => $functionNameForUnknown, + ]); + + $className = $this->namespaceRegistry->getObject($shape); + $this->imports[] = $className; + + $this->functions[$functionName] = $this->createPopulateMethod($functionName, strtr(implode("\n", $body), [ + 'INPUT' => $input, + 'CLASS_NAME' => $className->getName(), + ]), $shape); + } + + return strtr($required ? '$this->FUNCTION_NAME(INPUT)' : 'empty(INPUT) ? null : $this->FUNCTION_NAME(INPUT)', [ + 'INPUT' => $input, + 'FUNCTION_NAME' => $functionName, + ]); + } + private function parseResponseStructure(StructureShape $shape, string $input, bool $required): string { $functionName = 'populateResult' . ucfirst($shape->getName()); @@ -341,7 +393,7 @@ private function parseResponseList(ListShape $shape, string $input, bool $requir if (!isset($this->generatedFunctions[$functionName])) { // prevent recursion $this->generatedFunctions[$functionName] = true; - if ($shapeMember->getShape() instanceof StructureShape || $shapeMember->getShape() instanceof ListShape || $shapeMember->getShape() instanceof MapShape) { + if ($shapeMember->getShape() instanceof ObjectShape || $shapeMember->getShape() instanceof ListShape || $shapeMember->getShape() instanceof MapShape) { if (!empty($shapeMember->getShape()->getEnum())) { $className = $this->namespaceRegistry->getEnum($shapeMember->getShape()); $body = strtr(' @@ -444,9 +496,7 @@ private function parseResponseMap(MapShape $shape, string $input, bool $required $keyVar = '(string) $name'; } - // We need to use array keys - if ($shapeValue->getShape() instanceof StructureShape) { - $body = ' + $body = ' $items = []; foreach ($json as $name => $value) { CLEAN_KEY_CODE @@ -456,28 +506,17 @@ private function parseResponseMap(MapShape $shape, string $input, bool $required return $items; '; - $this->functions[$functionName] = $this->createPopulateMethod($functionName, strtr($body, [ - 'BUILDER_CODE' => $this->parseResponseStructure($shapeValue->getShape(), '$value', true), - 'CLEAN_KEY_CODE' => $cleanKeyCode, - 'KEY_VAR' => $keyVar, - ]), $shape); + // We need to use array keys + if ($shapeValue->getShape() instanceof StructureShape) { + $code = $this->parseResponseStructure($shapeValue->getShape(), '$value', true); } else { - $body = ' - $items = []; - foreach ($json as $name => $value) { - CLEAN_KEY_CODE - $items[KEY_VAR] = BUILDER_CODE; - } - - return $items; - '; - - $this->functions[$functionName] = $this->createPopulateMethod($functionName, strtr($body, [ - 'BUILDER_CODE' => $this->parseElement('$value', $shapeValue->getShape(), true, $inObject), - 'CLEAN_KEY_CODE' => $cleanKeyCode, - 'KEY_VAR' => $keyVar, - ]), $shape); + $code = $this->parseElement('$value', $shapeValue->getShape(), true, $inObject); } + $this->functions[$functionName] = $this->createPopulateMethod($functionName, strtr($body, [ + 'BUILDER_CODE' => $code, + 'CLEAN_KEY_CODE' => $cleanKeyCode, + 'KEY_VAR' => $keyVar, + ]), $shape); } else { if (!empty($shape->getKey()->getShape()->getEnum())) { $className = $this->namespaceRegistry->getEnum($shape->getKey()->getShape()); @@ -497,7 +536,7 @@ private function parseResponseMap(MapShape $shape, string $input, bool $required $keyVar = strtr('$name = $item[MAP_KEY]', ['MAP_KEY' => var_export($locationName, true)]); } $inputAccessorName = $this->getInputAccessorName($shapeValue); - if ($shapeValue->getShape() instanceof StructureShape) { + if ($shapeValue->getShape() instanceof ObjectShape) { $body = ' $items = []; foreach ($json as $item) { diff --git a/src/CodeGenerator/src/Generator/ResponseParser/RestXmlParser.php b/src/CodeGenerator/src/Generator/ResponseParser/RestXmlParser.php index 729053768..7bdd13278 100644 --- a/src/CodeGenerator/src/Generator/ResponseParser/RestXmlParser.php +++ b/src/CodeGenerator/src/Generator/ResponseParser/RestXmlParser.php @@ -11,6 +11,7 @@ use AsyncAws\CodeGenerator\Definition\Shape; use AsyncAws\CodeGenerator\Definition\StructureMember; use AsyncAws\CodeGenerator\Definition\StructureShape; +use AsyncAws\CodeGenerator\Definition\UnionShape; use AsyncAws\CodeGenerator\Generator\CodeGenerator\TypeGenerator; use AsyncAws\CodeGenerator\Generator\Composer\RequirementsRegistry; use AsyncAws\CodeGenerator\Generator\EnumGenerator; @@ -180,6 +181,8 @@ private function parseXmlElement(string $input, Shape $shape, bool $required, bo switch (true) { case $shape instanceof ListShape: return $this->parseXmlResponseList($shape, $input, $required, $inObject); + case $shape instanceof UnionShape: + return $this->parseXmlResponseUnion($shape, $input, $required); case $shape instanceof StructureShape: return $this->parseXmlResponseStructure($shape, $input, $required); case $shape instanceof MapShape: @@ -212,6 +215,57 @@ private function parseXmlElement(string $input, Shape $shape, bool $required, bo throw new \RuntimeException(\sprintf('Type %s is not yet implemented', $shape->getType())); } + private function parseXmlResponseUnion(UnionShape $shape, string $input, bool $required): string + { + $functionName = 'populateResult' . ucfirst($shape->getName()); + if (!isset($this->generatedFunctions[$functionName])) { + // prevent recursion + $this->generatedFunctions[$functionName] = true; + + $body = []; + foreach ($shape->getChildren() as $child) { + $body[] = strtr('if (0 !== PROPERTY_ACCESSOR->count()) { + return PROPERTY_ACCESSOR; + }', [ + 'PROPERTY_NAME' => $this->getInputAccessor('$xml', $child->getMembers()[0]), + 'PROPERTY_ACCESSOR' => $this->parseXmlElement('$xml', $child, true, true), + ]); + $className = $this->namespaceRegistry->getObject($child); + $this->imports[] = $className; + } + + // Generate fallback for unknown member + $childForUnknown = $shape->getChildForUnknown(); + $functionNameForUnknown = 'populateResult' . ucfirst($childForUnknown->getName()); + $className = $this->namespaceRegistry->getObject($childForUnknown); + $this->imports[] = $className; + $this->functions[$functionNameForUnknown] = $this->createPopulateMethod($functionNameForUnknown, strtr('return new CLASS_NAME([\'xml\' => $xml]);', [ + 'INPUT' => $input, + 'CLASS_NAME' => $className->getName(), + ]), $shape); + + $body[] = strtr('return $this->FUNCTION_NAME($xml);', [ + 'FUNCTION_NAME' => $functionNameForUnknown, + ]); + + $this->imports[] = ClassName::create('AsyncAws\Core\Exception', 'InvalidArgument'); + $body[] = 'throw new InvalidArgument(\'Invalid union input\');'; + + $className = $this->namespaceRegistry->getObject($shape); + $this->imports[] = $className; + + $this->functions[$functionName] = $this->createPopulateMethod($functionName, strtr(implode("\n", $body), [ + 'INPUT' => $input, + 'CLASS_NAME' => $className->getName(), + ]), $shape); + } + + return strtr($required ? '$this->FUNCTION_NAME(INPUT)' : '0 === INPUT->count() ? null : $this->FUNCTION_NAME(INPUT)', [ + 'INPUT' => $input, + 'FUNCTION_NAME' => $functionName, + ]); + } + private function parseXmlResponseStructure(StructureShape $shape, string $input, bool $required): string { $functionName = 'populateResult' . ucfirst($shape->getName()); @@ -223,7 +277,12 @@ private function parseXmlResponseStructure(StructureShape $shape, string $input, foreach ($shape->getMembers() as $member) { $properties[] = strtr('PROPERTY_NAME => PROPERTY_ACCESSOR,', [ 'PROPERTY_NAME' => var_export($member->getName(), true), - 'PROPERTY_ACCESSOR' => $this->parseXmlElement($this->getInputAccessor('$xml', $member), $member->getShape(), $member->isRequired(), true), + 'PROPERTY_ACCESSOR' => $this->parseXmlElement( + $this->getInputAccessor('$xml', $member), + $member->getShape(), + $member->isRequired(), + true + ), ]); } diff --git a/src/CodeGenerator/src/Generator/ResultGenerator.php b/src/CodeGenerator/src/Generator/ResultGenerator.php index bbfc02464..be5919324 100644 --- a/src/CodeGenerator/src/Generator/ResultGenerator.php +++ b/src/CodeGenerator/src/Generator/ResultGenerator.php @@ -6,6 +6,7 @@ use AsyncAws\CodeGenerator\Definition\ListShape; use AsyncAws\CodeGenerator\Definition\MapShape; +use AsyncAws\CodeGenerator\Definition\ObjectShape; use AsyncAws\CodeGenerator\Definition\Operation; use AsyncAws\CodeGenerator\Definition\StructureShape; use AsyncAws\CodeGenerator\Generator\CodeGenerator\PopulatorGenerator; @@ -102,14 +103,14 @@ private function addUse(StructureShape $shape, ClassBuilder $classBuilder, array $classBuilder->addUse($this->namespaceRegistry->getEnum($memberShape)->getFqdn()); } - if ($memberShape instanceof StructureShape) { + if ($memberShape instanceof ObjectShape) { $fqdn = $this->namespaceRegistry->getObject($memberShape)->getFqdn(); if (!\in_array($fqdn, $addedFqdn)) { $addedFqdn[] = $fqdn; $classBuilder->addUse($fqdn); } } elseif ($memberShape instanceof MapShape) { - if (($valueShape = $memberShape->getValue()->getShape()) instanceof StructureShape) { + if (($valueShape = $memberShape->getValue()->getShape()) instanceof ObjectShape) { $fqdn = $this->namespaceRegistry->getObject($valueShape)->getFqdn(); if (!\in_array($fqdn, $addedFqdn)) { $addedFqdn[] = $fqdn; @@ -120,7 +121,7 @@ private function addUse(StructureShape $shape, ClassBuilder $classBuilder, array $classBuilder->addUse($this->namespaceRegistry->getEnum($valueShape)->getFqdn()); } } elseif ($memberShape instanceof ListShape) { - if (($memberShape = $memberShape->getMember()->getShape()) instanceof StructureShape) { + if (($memberShape = $memberShape->getMember()->getShape()) instanceof ObjectShape) { $fqdn = $this->namespaceRegistry->getObject($memberShape)->getFqdn(); if (!\in_array($fqdn, $addedFqdn)) { $addedFqdn[] = $fqdn; diff --git a/src/CodeGenerator/src/Generator/ServiceGenerator.php b/src/CodeGenerator/src/Generator/ServiceGenerator.php index cfb8d3e21..ab0d1190b 100644 --- a/src/CodeGenerator/src/Generator/ServiceGenerator.php +++ b/src/CodeGenerator/src/Generator/ServiceGenerator.php @@ -81,6 +81,11 @@ class ServiceGenerator */ private $object; + /** + * @var ?ShapeUsageHelper + */ + private $shapeUsage; + /** * @var ?TypeGenerator */ @@ -149,7 +154,7 @@ public function test(): TestGenerator public function populator(): PopulatorGenerator { - return $this->populator ?? $this->populator = new PopulatorGenerator($this->classRegistry, $this->namespaceRegistry, $this->requirementsRegistry, $this->object(), $this->managedOperations, $this->type(), $this->enum(), $this->parserProvider()); + return $this->populator ?? $this->populator = new PopulatorGenerator($this->classRegistry, $this->namespaceRegistry, $this->requirementsRegistry, $this->object(), $this->shapeUsage(), $this->type(), $this->enum(), $this->parserProvider()); } public function result(): ResultGenerator @@ -169,7 +174,7 @@ public function parserProvider(): ParserProvider public function input(): InputGenerator { - return $this->input ?? $this->input = new InputGenerator($this->classRegistry, $this->namespaceRegistry, $this->requirementsRegistry, $this->object(), $this->managedOperations, $this->type(), $this->enum(), $this->hook()); + return $this->input ?? $this->input = new InputGenerator($this->classRegistry, $this->namespaceRegistry, $this->requirementsRegistry, $this->object(), $this->shapeUsage(), $this->type(), $this->enum(), $this->hook()); } public function type(): TypeGenerator @@ -179,7 +184,7 @@ public function type(): TypeGenerator public function enum(): EnumGenerator { - return $this->enum ?? $this->enum = new EnumGenerator($this->classRegistry, $this->namespaceRegistry, $this->managedOperations); + return $this->enum ?? $this->enum = new EnumGenerator($this->classRegistry, $this->namespaceRegistry, $this->shapeUsage()); } public function hook(): HookGenerator @@ -189,7 +194,12 @@ public function hook(): HookGenerator public function object(): ObjectGenerator { - return $this->object ?? $this->object = new ObjectGenerator($this->classRegistry, $this->namespaceRegistry, $this->requirementsRegistry, $this->managedOperations, $this->type(), $this->enum()); + return $this->object ?? $this->object = new ObjectGenerator($this->classRegistry, $this->namespaceRegistry, $this->requirementsRegistry, $this->shapeUsage(), $this->type(), $this->enum()); + } + + public function shapeUsage(): ShapeUsageHelper + { + return $this->shapeUsage ?? $this->shapeUsage = new ShapeUsageHelper($this->managedOperations); } public function getNamespaceRegistry(): NamespaceRegistry diff --git a/src/CodeGenerator/src/Generator/ShapeUsageHelper.php b/src/CodeGenerator/src/Generator/ShapeUsageHelper.php new file mode 100644 index 000000000..4ab579429 --- /dev/null +++ b/src/CodeGenerator/src/Generator/ShapeUsageHelper.php @@ -0,0 +1,116 @@ + + * + * @internal + */ +class ShapeUsageHelper +{ + /** + * @var list + */ + private $managedOperations = []; + + /** + * @var array|null + */ + private $usedShapedInput; + + /** + * @var array|null + */ + private $usedShapedOutput; + + public function __construct(array $managedOperations) + { + $this->managedOperations = $managedOperations; + } + + public function isShapeUsedInput(ObjectShape $shape): bool + { + if (null === $this->usedShapedInput) { + $service = $shape->getService(); + $this->usedShapedInput = []; + + foreach ($this->managedOperations as $method) { + if (null !== $operation = $service->getOperation($method)) { + $this->walkShape($operation->getInput(), $this->usedShapedInput); + } + if (null !== $waiter = $service->getWaiter($method)) { + $this->walkShape($waiter->getOperation()->getInput(), $this->usedShapedInput); + } + } + } + + return $this->usedShapedInput[$shape->getName()] ?? false; + } + + public function isShapeUsedOutput(Shape $shape): bool + { + if (null === $this->usedShapedOutput) { + $service = $shape->getService(); + $this->usedShapedOutput = []; + + foreach ($this->managedOperations as $method) { + if (null !== $operation = $service->getOperation($method)) { + $this->walkShape($operation->getOutput(), $this->usedShapedOutput); + foreach ($operation->getErrors() as $error) { + $this->walkShape($error, $this->usedShapedOutput); + } + } + if (null !== $waiter = $service->getWaiter($method)) { + $this->walkShape($waiter->getOperation()->getOutput(), $this->usedShapedOutput); + foreach ($waiter->getOperation()->getErrors() as $error) { + $this->walkShape($error, $this->usedShapedOutput); + } + } + } + } + + return $this->usedShapedOutput[$shape->getName()] ?? false; + } + + /** + * @param array $marker + */ + private function walkShape(?Shape $shape, array &$marker): void + { + if (null === $shape) { + return; + } + if (isset($marker[$shape->getName()])) { + // Node already visited + return; + } + + $marker[$shape->getName()] = true; + if ($shape instanceof StructureShape) { + foreach ($shape->getMembers() as $member) { + $this->walkShape($member->getShape(), $marker); + } + } elseif ($shape instanceof UnionShape) { + foreach ($shape->getChildren() as $child) { + $this->walkShape($child, $marker); + } + } elseif ($shape instanceof ListShape) { + $this->walkShape($shape->getMember()->getShape(), $marker); + } elseif ($shape instanceof MapShape) { + $this->walkShape($shape->getKey()->getShape(), $marker); + $this->walkShape($shape->getValue()->getShape(), $marker); + } + } +} diff --git a/src/Service/RdsDataService/CHANGELOG.md b/src/Service/RdsDataService/CHANGELOG.md index 09df4fa2c..5084db6a2 100644 --- a/src/Service/RdsDataService/CHANGELOG.md +++ b/src/Service/RdsDataService/CHANGELOG.md @@ -2,6 +2,10 @@ ## NOT RELEASED +### BC-BREAK + +- Generic `Field` and `ArrayValue` value objects now have strict types for all their getters and setters. This may lead to type errors in existing code if you were passing or expecting different types. + ### Added - Add `UNKNOWN_TO_SDK` value to enums that is used when the API returns an value that is not (or not yet) known by the AsyncAws diff --git a/src/Service/RdsDataService/composer.json b/src/Service/RdsDataService/composer.json index b2bcf6eed..c4c4eaf29 100644 --- a/src/Service/RdsDataService/composer.json +++ b/src/Service/RdsDataService/composer.json @@ -34,7 +34,7 @@ }, "extra": { "branch-alias": { - "dev-master": "2.4-dev" + "dev-master": "3.0-dev" } } } diff --git a/src/Service/RdsDataService/src/Result/BatchExecuteStatementResponse.php b/src/Service/RdsDataService/src/Result/BatchExecuteStatementResponse.php index 4a911993b..4bbc7af7f 100644 --- a/src/Service/RdsDataService/src/Result/BatchExecuteStatementResponse.php +++ b/src/Service/RdsDataService/src/Result/BatchExecuteStatementResponse.php @@ -5,7 +5,21 @@ use AsyncAws\Core\Response; use AsyncAws\Core\Result; use AsyncAws\RdsDataService\ValueObject\ArrayValue; +use AsyncAws\RdsDataService\ValueObject\ArrayValueMemberArrayValues; +use AsyncAws\RdsDataService\ValueObject\ArrayValueMemberBooleanValues; +use AsyncAws\RdsDataService\ValueObject\ArrayValueMemberDoubleValues; +use AsyncAws\RdsDataService\ValueObject\ArrayValueMemberLongValues; +use AsyncAws\RdsDataService\ValueObject\ArrayValueMemberStringValues; +use AsyncAws\RdsDataService\ValueObject\ArrayValueMemberUnknownToSdk; use AsyncAws\RdsDataService\ValueObject\Field; +use AsyncAws\RdsDataService\ValueObject\FieldMemberArrayValue; +use AsyncAws\RdsDataService\ValueObject\FieldMemberBlobValue; +use AsyncAws\RdsDataService\ValueObject\FieldMemberBooleanValue; +use AsyncAws\RdsDataService\ValueObject\FieldMemberDoubleValue; +use AsyncAws\RdsDataService\ValueObject\FieldMemberIsNull; +use AsyncAws\RdsDataService\ValueObject\FieldMemberLongValue; +use AsyncAws\RdsDataService\ValueObject\FieldMemberStringValue; +use AsyncAws\RdsDataService\ValueObject\FieldMemberUnknownToSdk; use AsyncAws\RdsDataService\ValueObject\UpdateResult; /** @@ -52,15 +66,65 @@ private function populateResultArrayOfArray(array $json): array private function populateResultArrayValue(array $json): ArrayValue { - return new ArrayValue([ - 'booleanValues' => !isset($json['booleanValues']) ? null : $this->populateResultBooleanArray($json['booleanValues']), - 'longValues' => !isset($json['longValues']) ? null : $this->populateResultLongArray($json['longValues']), - 'doubleValues' => !isset($json['doubleValues']) ? null : $this->populateResultDoubleArray($json['doubleValues']), - 'stringValues' => !isset($json['stringValues']) ? null : $this->populateResultStringArray($json['stringValues']), - 'arrayValues' => !isset($json['arrayValues']) ? null : $this->populateResultArrayOfArray($json['arrayValues']), + if (isset($json['booleanValues'])) { + return $this->populateResultArrayValueMemberBooleanValues($json); + } + if (isset($json['longValues'])) { + return $this->populateResultArrayValueMemberLongValues($json); + } + if (isset($json['doubleValues'])) { + return $this->populateResultArrayValueMemberDoubleValues($json); + } + if (isset($json['stringValues'])) { + return $this->populateResultArrayValueMemberStringValues($json); + } + if (isset($json['arrayValues'])) { + return $this->populateResultArrayValueMemberArrayValues($json); + } + + return $this->populateResultArrayValueMemberUnknownToSdk($json); + } + + private function populateResultArrayValueMemberArrayValues(array $json): ArrayValueMemberArrayValues + { + return new ArrayValueMemberArrayValues([ + 'arrayValues' => $this->populateResultArrayOfArray($json['arrayValues']), + ]); + } + + private function populateResultArrayValueMemberBooleanValues(array $json): ArrayValueMemberBooleanValues + { + return new ArrayValueMemberBooleanValues([ + 'booleanValues' => $this->populateResultBooleanArray($json['booleanValues']), + ]); + } + + private function populateResultArrayValueMemberDoubleValues(array $json): ArrayValueMemberDoubleValues + { + return new ArrayValueMemberDoubleValues([ + 'doubleValues' => $this->populateResultDoubleArray($json['doubleValues']), ]); } + private function populateResultArrayValueMemberLongValues(array $json): ArrayValueMemberLongValues + { + return new ArrayValueMemberLongValues([ + 'longValues' => $this->populateResultLongArray($json['longValues']), + ]); + } + + private function populateResultArrayValueMemberStringValues(array $json): ArrayValueMemberStringValues + { + return new ArrayValueMemberStringValues([ + 'stringValues' => $this->populateResultStringArray($json['stringValues']), + ]); + } + + private function populateResultArrayValueMemberUnknownToSdk(array $json): ArrayValue + { + return new ArrayValueMemberUnknownToSdk($json); + } + /** * @return bool[] */ @@ -95,15 +159,29 @@ private function populateResultDoubleArray(array $json): array private function populateResultField(array $json): Field { - return new Field([ - 'isNull' => isset($json['isNull']) ? filter_var($json['isNull'], \FILTER_VALIDATE_BOOLEAN) : null, - 'booleanValue' => isset($json['booleanValue']) ? filter_var($json['booleanValue'], \FILTER_VALIDATE_BOOLEAN) : null, - 'longValue' => isset($json['longValue']) ? (int) $json['longValue'] : null, - 'doubleValue' => isset($json['doubleValue']) ? (float) $json['doubleValue'] : null, - 'stringValue' => isset($json['stringValue']) ? (string) $json['stringValue'] : null, - 'blobValue' => isset($json['blobValue']) ? base64_decode((string) $json['blobValue']) : null, - 'arrayValue' => empty($json['arrayValue']) ? null : $this->populateResultArrayValue($json['arrayValue']), - ]); + if (isset($json['isNull'])) { + return $this->populateResultFieldMemberIsNull($json); + } + if (isset($json['booleanValue'])) { + return $this->populateResultFieldMemberBooleanValue($json); + } + if (isset($json['longValue'])) { + return $this->populateResultFieldMemberLongValue($json); + } + if (isset($json['doubleValue'])) { + return $this->populateResultFieldMemberDoubleValue($json); + } + if (isset($json['stringValue'])) { + return $this->populateResultFieldMemberStringValue($json); + } + if (isset($json['blobValue'])) { + return $this->populateResultFieldMemberBlobValue($json); + } + if (isset($json['arrayValue'])) { + return $this->populateResultFieldMemberArrayValue($json); + } + + return $this->populateResultFieldMemberUnknownToSdk($json); } /** @@ -119,6 +197,60 @@ private function populateResultFieldList(array $json): array return $items; } + private function populateResultFieldMemberArrayValue(array $json): FieldMemberArrayValue + { + return new FieldMemberArrayValue([ + 'arrayValue' => $this->populateResultArrayValue($json['arrayValue']), + ]); + } + + private function populateResultFieldMemberBlobValue(array $json): FieldMemberBlobValue + { + return new FieldMemberBlobValue([ + 'blobValue' => base64_decode((string) $json['blobValue']), + ]); + } + + private function populateResultFieldMemberBooleanValue(array $json): FieldMemberBooleanValue + { + return new FieldMemberBooleanValue([ + 'booleanValue' => filter_var($json['booleanValue'], \FILTER_VALIDATE_BOOLEAN), + ]); + } + + private function populateResultFieldMemberDoubleValue(array $json): FieldMemberDoubleValue + { + return new FieldMemberDoubleValue([ + 'doubleValue' => (float) $json['doubleValue'], + ]); + } + + private function populateResultFieldMemberIsNull(array $json): FieldMemberIsNull + { + return new FieldMemberIsNull([ + 'isNull' => filter_var($json['isNull'], \FILTER_VALIDATE_BOOLEAN), + ]); + } + + private function populateResultFieldMemberLongValue(array $json): FieldMemberLongValue + { + return new FieldMemberLongValue([ + 'longValue' => (int) $json['longValue'], + ]); + } + + private function populateResultFieldMemberStringValue(array $json): FieldMemberStringValue + { + return new FieldMemberStringValue([ + 'stringValue' => (string) $json['stringValue'], + ]); + } + + private function populateResultFieldMemberUnknownToSdk(array $json): Field + { + return new FieldMemberUnknownToSdk($json); + } + /** * @return int[] */ diff --git a/src/Service/RdsDataService/src/Result/ExecuteStatementResponse.php b/src/Service/RdsDataService/src/Result/ExecuteStatementResponse.php index 87afb9c7d..8682a9759 100644 --- a/src/Service/RdsDataService/src/Result/ExecuteStatementResponse.php +++ b/src/Service/RdsDataService/src/Result/ExecuteStatementResponse.php @@ -5,8 +5,22 @@ use AsyncAws\Core\Response; use AsyncAws\Core\Result; use AsyncAws\RdsDataService\ValueObject\ArrayValue; +use AsyncAws\RdsDataService\ValueObject\ArrayValueMemberArrayValues; +use AsyncAws\RdsDataService\ValueObject\ArrayValueMemberBooleanValues; +use AsyncAws\RdsDataService\ValueObject\ArrayValueMemberDoubleValues; +use AsyncAws\RdsDataService\ValueObject\ArrayValueMemberLongValues; +use AsyncAws\RdsDataService\ValueObject\ArrayValueMemberStringValues; +use AsyncAws\RdsDataService\ValueObject\ArrayValueMemberUnknownToSdk; use AsyncAws\RdsDataService\ValueObject\ColumnMetadata; use AsyncAws\RdsDataService\ValueObject\Field; +use AsyncAws\RdsDataService\ValueObject\FieldMemberArrayValue; +use AsyncAws\RdsDataService\ValueObject\FieldMemberBlobValue; +use AsyncAws\RdsDataService\ValueObject\FieldMemberBooleanValue; +use AsyncAws\RdsDataService\ValueObject\FieldMemberDoubleValue; +use AsyncAws\RdsDataService\ValueObject\FieldMemberIsNull; +use AsyncAws\RdsDataService\ValueObject\FieldMemberLongValue; +use AsyncAws\RdsDataService\ValueObject\FieldMemberStringValue; +use AsyncAws\RdsDataService\ValueObject\FieldMemberUnknownToSdk; /** * The response elements represent the output of a request to run a SQL statement against a database. @@ -129,15 +143,65 @@ private function populateResultArrayOfArray(array $json): array private function populateResultArrayValue(array $json): ArrayValue { - return new ArrayValue([ - 'booleanValues' => !isset($json['booleanValues']) ? null : $this->populateResultBooleanArray($json['booleanValues']), - 'longValues' => !isset($json['longValues']) ? null : $this->populateResultLongArray($json['longValues']), - 'doubleValues' => !isset($json['doubleValues']) ? null : $this->populateResultDoubleArray($json['doubleValues']), - 'stringValues' => !isset($json['stringValues']) ? null : $this->populateResultStringArray($json['stringValues']), - 'arrayValues' => !isset($json['arrayValues']) ? null : $this->populateResultArrayOfArray($json['arrayValues']), + if (isset($json['booleanValues'])) { + return $this->populateResultArrayValueMemberBooleanValues($json); + } + if (isset($json['longValues'])) { + return $this->populateResultArrayValueMemberLongValues($json); + } + if (isset($json['doubleValues'])) { + return $this->populateResultArrayValueMemberDoubleValues($json); + } + if (isset($json['stringValues'])) { + return $this->populateResultArrayValueMemberStringValues($json); + } + if (isset($json['arrayValues'])) { + return $this->populateResultArrayValueMemberArrayValues($json); + } + + return $this->populateResultArrayValueMemberUnknownToSdk($json); + } + + private function populateResultArrayValueMemberArrayValues(array $json): ArrayValueMemberArrayValues + { + return new ArrayValueMemberArrayValues([ + 'arrayValues' => $this->populateResultArrayOfArray($json['arrayValues']), + ]); + } + + private function populateResultArrayValueMemberBooleanValues(array $json): ArrayValueMemberBooleanValues + { + return new ArrayValueMemberBooleanValues([ + 'booleanValues' => $this->populateResultBooleanArray($json['booleanValues']), + ]); + } + + private function populateResultArrayValueMemberDoubleValues(array $json): ArrayValueMemberDoubleValues + { + return new ArrayValueMemberDoubleValues([ + 'doubleValues' => $this->populateResultDoubleArray($json['doubleValues']), ]); } + private function populateResultArrayValueMemberLongValues(array $json): ArrayValueMemberLongValues + { + return new ArrayValueMemberLongValues([ + 'longValues' => $this->populateResultLongArray($json['longValues']), + ]); + } + + private function populateResultArrayValueMemberStringValues(array $json): ArrayValueMemberStringValues + { + return new ArrayValueMemberStringValues([ + 'stringValues' => $this->populateResultStringArray($json['stringValues']), + ]); + } + + private function populateResultArrayValueMemberUnknownToSdk(array $json): ArrayValue + { + return new ArrayValueMemberUnknownToSdk($json); + } + /** * @return bool[] */ @@ -192,15 +256,29 @@ private function populateResultDoubleArray(array $json): array private function populateResultField(array $json): Field { - return new Field([ - 'isNull' => isset($json['isNull']) ? filter_var($json['isNull'], \FILTER_VALIDATE_BOOLEAN) : null, - 'booleanValue' => isset($json['booleanValue']) ? filter_var($json['booleanValue'], \FILTER_VALIDATE_BOOLEAN) : null, - 'longValue' => isset($json['longValue']) ? (int) $json['longValue'] : null, - 'doubleValue' => isset($json['doubleValue']) ? (float) $json['doubleValue'] : null, - 'stringValue' => isset($json['stringValue']) ? (string) $json['stringValue'] : null, - 'blobValue' => isset($json['blobValue']) ? base64_decode((string) $json['blobValue']) : null, - 'arrayValue' => empty($json['arrayValue']) ? null : $this->populateResultArrayValue($json['arrayValue']), - ]); + if (isset($json['isNull'])) { + return $this->populateResultFieldMemberIsNull($json); + } + if (isset($json['booleanValue'])) { + return $this->populateResultFieldMemberBooleanValue($json); + } + if (isset($json['longValue'])) { + return $this->populateResultFieldMemberLongValue($json); + } + if (isset($json['doubleValue'])) { + return $this->populateResultFieldMemberDoubleValue($json); + } + if (isset($json['stringValue'])) { + return $this->populateResultFieldMemberStringValue($json); + } + if (isset($json['blobValue'])) { + return $this->populateResultFieldMemberBlobValue($json); + } + if (isset($json['arrayValue'])) { + return $this->populateResultFieldMemberArrayValue($json); + } + + return $this->populateResultFieldMemberUnknownToSdk($json); } /** @@ -216,6 +294,60 @@ private function populateResultFieldList(array $json): array return $items; } + private function populateResultFieldMemberArrayValue(array $json): FieldMemberArrayValue + { + return new FieldMemberArrayValue([ + 'arrayValue' => $this->populateResultArrayValue($json['arrayValue']), + ]); + } + + private function populateResultFieldMemberBlobValue(array $json): FieldMemberBlobValue + { + return new FieldMemberBlobValue([ + 'blobValue' => base64_decode((string) $json['blobValue']), + ]); + } + + private function populateResultFieldMemberBooleanValue(array $json): FieldMemberBooleanValue + { + return new FieldMemberBooleanValue([ + 'booleanValue' => filter_var($json['booleanValue'], \FILTER_VALIDATE_BOOLEAN), + ]); + } + + private function populateResultFieldMemberDoubleValue(array $json): FieldMemberDoubleValue + { + return new FieldMemberDoubleValue([ + 'doubleValue' => (float) $json['doubleValue'], + ]); + } + + private function populateResultFieldMemberIsNull(array $json): FieldMemberIsNull + { + return new FieldMemberIsNull([ + 'isNull' => filter_var($json['isNull'], \FILTER_VALIDATE_BOOLEAN), + ]); + } + + private function populateResultFieldMemberLongValue(array $json): FieldMemberLongValue + { + return new FieldMemberLongValue([ + 'longValue' => (int) $json['longValue'], + ]); + } + + private function populateResultFieldMemberStringValue(array $json): FieldMemberStringValue + { + return new FieldMemberStringValue([ + 'stringValue' => (string) $json['stringValue'], + ]); + } + + private function populateResultFieldMemberUnknownToSdk(array $json): Field + { + return new FieldMemberUnknownToSdk($json); + } + /** * @return int[] */ diff --git a/src/Service/RdsDataService/src/ValueObject/ArrayValue.php b/src/Service/RdsDataService/src/ValueObject/ArrayValue.php index 5ad123acf..33611181f 100644 --- a/src/Service/RdsDataService/src/ValueObject/ArrayValue.php +++ b/src/Service/RdsDataService/src/ValueObject/ArrayValue.php @@ -2,165 +2,54 @@ namespace AsyncAws\RdsDataService\ValueObject; +use AsyncAws\Core\Exception\InvalidArgument; + /** * Contains an array. + * + * @psalm-inheritors ArrayValueMemberBooleanValues|ArrayValueMemberLongValues|ArrayValueMemberDoubleValues|ArrayValueMemberStringValues|ArrayValueMemberArrayValues|ArrayValueMemberUnknownToSdk */ -final class ArrayValue +abstract class ArrayValue { - /** - * An array of Boolean values. - * - * @var bool[]|null - */ - private $booleanValues; - - /** - * An array of integers. - * - * @var int[]|null - */ - private $longValues; - - /** - * An array of floating-point numbers. - * - * @var float[]|null - */ - private $doubleValues; - - /** - * An array of strings. - * - * @var string[]|null - */ - private $stringValues; - - /** - * An array of arrays. - * - * @var ArrayValue[]|null - */ - private $arrayValues; - - /** - * @param array{ - * booleanValues?: bool[]|null, - * longValues?: int[]|null, - * doubleValues?: float[]|null, - * stringValues?: string[]|null, - * arrayValues?: array|null, - * } $input - */ - public function __construct(array $input) - { - $this->booleanValues = $input['booleanValues'] ?? null; - $this->longValues = $input['longValues'] ?? null; - $this->doubleValues = $input['doubleValues'] ?? null; - $this->stringValues = $input['stringValues'] ?? null; - $this->arrayValues = isset($input['arrayValues']) ? array_map([ArrayValue::class, 'create'], $input['arrayValues']) : null; - } - /** * @param array{ - * booleanValues?: bool[]|null, - * longValues?: int[]|null, - * doubleValues?: float[]|null, - * stringValues?: string[]|null, - * arrayValues?: array|null, + * booleanValues: bool[], + * }|array{ + * longValues: int[], + * }|array{ + * doubleValues: float[], + * }|array{ + * stringValues: string[], + * }|array{ + * arrayValues: array, * }|ArrayValue $input */ public static function create($input): self { - return $input instanceof self ? $input : new self($input); - } - - /** - * @return ArrayValue[] - */ - public function getArrayValues(): array - { - return $this->arrayValues ?? []; - } - - /** - * @return bool[] - */ - public function getBooleanValues(): array - { - return $this->booleanValues ?? []; - } - - /** - * @return float[] - */ - public function getDoubleValues(): array - { - return $this->doubleValues ?? []; - } - - /** - * @return int[] - */ - public function getLongValues(): array - { - return $this->longValues ?? []; - } - - /** - * @return string[] - */ - public function getStringValues(): array - { - return $this->stringValues ?? []; - } - - /** - * @internal - */ - public function requestBody(): array - { - $payload = []; - if (null !== $v = $this->booleanValues) { - $index = -1; - $payload['booleanValues'] = []; - foreach ($v as $listValue) { - ++$index; - $payload['booleanValues'][$index] = (bool) $listValue; - } + if ($input instanceof self) { + return $input; } - if (null !== $v = $this->longValues) { - $index = -1; - $payload['longValues'] = []; - foreach ($v as $listValue) { - ++$index; - $payload['longValues'][$index] = $listValue; - } + if (isset($input['booleanValues'])) { + return new ArrayValueMemberBooleanValues(['booleanValues' => $input['booleanValues']]); } - if (null !== $v = $this->doubleValues) { - $index = -1; - $payload['doubleValues'] = []; - foreach ($v as $listValue) { - ++$index; - $payload['doubleValues'][$index] = $listValue; - } + if (isset($input['longValues'])) { + return new ArrayValueMemberLongValues(['longValues' => $input['longValues']]); } - if (null !== $v = $this->stringValues) { - $index = -1; - $payload['stringValues'] = []; - foreach ($v as $listValue) { - ++$index; - $payload['stringValues'][$index] = $listValue; - } + if (isset($input['doubleValues'])) { + return new ArrayValueMemberDoubleValues(['doubleValues' => $input['doubleValues']]); } - if (null !== $v = $this->arrayValues) { - $index = -1; - $payload['arrayValues'] = []; - foreach ($v as $listValue) { - ++$index; - $payload['arrayValues'][$index] = $listValue->requestBody(); - } + if (isset($input['stringValues'])) { + return new ArrayValueMemberStringValues(['stringValues' => $input['stringValues']]); + } + if (isset($input['arrayValues'])) { + return new ArrayValueMemberArrayValues(['arrayValues' => $input['arrayValues']]); } - return $payload; + throw new InvalidArgument('Invalid union input'); } + + /** + * @internal + */ + abstract public function requestBody(): array; } diff --git a/src/Service/RdsDataService/src/ValueObject/ArrayValueMemberArrayValues.php b/src/Service/RdsDataService/src/ValueObject/ArrayValueMemberArrayValues.php new file mode 100644 index 000000000..b60cfdde2 --- /dev/null +++ b/src/Service/RdsDataService/src/ValueObject/ArrayValueMemberArrayValues.php @@ -0,0 +1,59 @@ +, + * } $input + */ + public function __construct(array $input) + { + $this->arrayValues = isset($input['arrayValues']) ? array_map([ArrayValue::class, 'create'], $input['arrayValues']) : $this->throwException(new InvalidArgument('Missing required field "arrayValues".')); + } + + /** + * @return ArrayValue[] + */ + public function getArrayValues(): array + { + return $this->arrayValues; + } + + /** + * @internal + */ + public function requestBody(): array + { + $payload = []; + $v = $this->arrayValues; + + $index = -1; + $payload['arrayValues'] = []; + foreach ($v as $listValue) { + ++$index; + $payload['arrayValues'][$index] = $listValue->requestBody(); + } + + return $payload; + } + + /** + * @return never + */ + private function throwException(\Throwable $exception) + { + throw $exception; + } +} diff --git a/src/Service/RdsDataService/src/ValueObject/ArrayValueMemberBooleanValues.php b/src/Service/RdsDataService/src/ValueObject/ArrayValueMemberBooleanValues.php new file mode 100644 index 000000000..1f6c6abe2 --- /dev/null +++ b/src/Service/RdsDataService/src/ValueObject/ArrayValueMemberBooleanValues.php @@ -0,0 +1,59 @@ +booleanValues = $input['booleanValues'] ?? $this->throwException(new InvalidArgument('Missing required field "booleanValues".')); + } + + /** + * @return bool[] + */ + public function getBooleanValues(): array + { + return $this->booleanValues; + } + + /** + * @internal + */ + public function requestBody(): array + { + $payload = []; + $v = $this->booleanValues; + + $index = -1; + $payload['booleanValues'] = []; + foreach ($v as $listValue) { + ++$index; + $payload['booleanValues'][$index] = (bool) $listValue; + } + + return $payload; + } + + /** + * @return never + */ + private function throwException(\Throwable $exception) + { + throw $exception; + } +} diff --git a/src/Service/RdsDataService/src/ValueObject/ArrayValueMemberDoubleValues.php b/src/Service/RdsDataService/src/ValueObject/ArrayValueMemberDoubleValues.php new file mode 100644 index 000000000..9ba17147f --- /dev/null +++ b/src/Service/RdsDataService/src/ValueObject/ArrayValueMemberDoubleValues.php @@ -0,0 +1,59 @@ +doubleValues = $input['doubleValues'] ?? $this->throwException(new InvalidArgument('Missing required field "doubleValues".')); + } + + /** + * @return float[] + */ + public function getDoubleValues(): array + { + return $this->doubleValues; + } + + /** + * @internal + */ + public function requestBody(): array + { + $payload = []; + $v = $this->doubleValues; + + $index = -1; + $payload['doubleValues'] = []; + foreach ($v as $listValue) { + ++$index; + $payload['doubleValues'][$index] = $listValue; + } + + return $payload; + } + + /** + * @return never + */ + private function throwException(\Throwable $exception) + { + throw $exception; + } +} diff --git a/src/Service/RdsDataService/src/ValueObject/ArrayValueMemberLongValues.php b/src/Service/RdsDataService/src/ValueObject/ArrayValueMemberLongValues.php new file mode 100644 index 000000000..e40f2c6bd --- /dev/null +++ b/src/Service/RdsDataService/src/ValueObject/ArrayValueMemberLongValues.php @@ -0,0 +1,59 @@ +longValues = $input['longValues'] ?? $this->throwException(new InvalidArgument('Missing required field "longValues".')); + } + + /** + * @return int[] + */ + public function getLongValues(): array + { + return $this->longValues; + } + + /** + * @internal + */ + public function requestBody(): array + { + $payload = []; + $v = $this->longValues; + + $index = -1; + $payload['longValues'] = []; + foreach ($v as $listValue) { + ++$index; + $payload['longValues'][$index] = $listValue; + } + + return $payload; + } + + /** + * @return never + */ + private function throwException(\Throwable $exception) + { + throw $exception; + } +} diff --git a/src/Service/RdsDataService/src/ValueObject/ArrayValueMemberStringValues.php b/src/Service/RdsDataService/src/ValueObject/ArrayValueMemberStringValues.php new file mode 100644 index 000000000..69c0fea72 --- /dev/null +++ b/src/Service/RdsDataService/src/ValueObject/ArrayValueMemberStringValues.php @@ -0,0 +1,59 @@ +stringValues = $input['stringValues'] ?? $this->throwException(new InvalidArgument('Missing required field "stringValues".')); + } + + /** + * @return string[] + */ + public function getStringValues(): array + { + return $this->stringValues; + } + + /** + * @internal + */ + public function requestBody(): array + { + $payload = []; + $v = $this->stringValues; + + $index = -1; + $payload['stringValues'] = []; + foreach ($v as $listValue) { + ++$index; + $payload['stringValues'][$index] = $listValue; + } + + return $payload; + } + + /** + * @return never + */ + private function throwException(\Throwable $exception) + { + throw $exception; + } +} diff --git a/src/Service/RdsDataService/src/ValueObject/ArrayValueMemberUnknownToSdk.php b/src/Service/RdsDataService/src/ValueObject/ArrayValueMemberUnknownToSdk.php new file mode 100644 index 000000000..f48ed137d --- /dev/null +++ b/src/Service/RdsDataService/src/ValueObject/ArrayValueMemberUnknownToSdk.php @@ -0,0 +1,39 @@ + + */ + private $unknown; + + /** + * @internal + * + * @param array $input + */ + public function __construct(array $input) + { + $this->unknown = $input; + } + + /** + * @return array + */ + public function getUnknown(): array + { + return $this->unknown; + } + + /** + * @internal + */ + public function requestBody(): array + { + throw new LogicException('request can not be generated for unknown object'); + } +} diff --git a/src/Service/RdsDataService/src/ValueObject/Field.php b/src/Service/RdsDataService/src/ValueObject/Field.php index 0e2171459..6bc6f5a30 100644 --- a/src/Service/RdsDataService/src/ValueObject/Field.php +++ b/src/Service/RdsDataService/src/ValueObject/Field.php @@ -2,161 +2,64 @@ namespace AsyncAws\RdsDataService\ValueObject; +use AsyncAws\Core\Exception\InvalidArgument; + /** * Contains a value. + * + * @psalm-inheritors FieldMemberIsNull|FieldMemberBooleanValue|FieldMemberLongValue|FieldMemberDoubleValue|FieldMemberStringValue|FieldMemberBlobValue|FieldMemberArrayValue|FieldMemberUnknownToSdk */ -final class Field +abstract class Field { - /** - * A NULL value. - * - * @var bool|null - */ - private $isNull; - - /** - * A value of Boolean data type. - * - * @var bool|null - */ - private $booleanValue; - - /** - * A value of long data type. - * - * @var int|null - */ - private $longValue; - - /** - * A value of double data type. - * - * @var float|null - */ - private $doubleValue; - - /** - * A value of string data type. - * - * @var string|null - */ - private $stringValue; - - /** - * A value of BLOB data type. - * - * @var string|null - */ - private $blobValue; - - /** - * An array of values. - * - * @var ArrayValue|null - */ - private $arrayValue; - /** * @param array{ - * isNull?: bool|null, - * booleanValue?: bool|null, - * longValue?: int|null, - * doubleValue?: float|null, - * stringValue?: string|null, - * blobValue?: string|null, - * arrayValue?: ArrayValue|array|null, - * } $input - */ - public function __construct(array $input) - { - $this->isNull = $input['isNull'] ?? null; - $this->booleanValue = $input['booleanValue'] ?? null; - $this->longValue = $input['longValue'] ?? null; - $this->doubleValue = $input['doubleValue'] ?? null; - $this->stringValue = $input['stringValue'] ?? null; - $this->blobValue = $input['blobValue'] ?? null; - $this->arrayValue = isset($input['arrayValue']) ? ArrayValue::create($input['arrayValue']) : null; - } - - /** - * @param array{ - * isNull?: bool|null, - * booleanValue?: bool|null, - * longValue?: int|null, - * doubleValue?: float|null, - * stringValue?: string|null, - * blobValue?: string|null, - * arrayValue?: ArrayValue|array|null, + * isNull: bool, + * }|array{ + * booleanValue: bool, + * }|array{ + * longValue: int, + * }|array{ + * doubleValue: float, + * }|array{ + * stringValue: string, + * }|array{ + * blobValue: string, + * }|array{ + * arrayValue: ArrayValue|array, * }|Field $input */ public static function create($input): self { - return $input instanceof self ? $input : new self($input); - } - - public function getArrayValue(): ?ArrayValue - { - return $this->arrayValue; - } - - public function getBlobValue(): ?string - { - return $this->blobValue; - } - - public function getBooleanValue(): ?bool - { - return $this->booleanValue; - } - - public function getDoubleValue(): ?float - { - return $this->doubleValue; - } - - public function getIsNull(): ?bool - { - return $this->isNull; - } - - public function getLongValue(): ?int - { - return $this->longValue; - } - - public function getStringValue(): ?string - { - return $this->stringValue; - } - - /** - * @internal - */ - public function requestBody(): array - { - $payload = []; - if (null !== $v = $this->isNull) { - $payload['isNull'] = (bool) $v; + if ($input instanceof self) { + return $input; } - if (null !== $v = $this->booleanValue) { - $payload['booleanValue'] = (bool) $v; + if (isset($input['isNull'])) { + return new FieldMemberIsNull(['isNull' => $input['isNull']]); } - if (null !== $v = $this->longValue) { - $payload['longValue'] = $v; + if (isset($input['booleanValue'])) { + return new FieldMemberBooleanValue(['booleanValue' => $input['booleanValue']]); } - if (null !== $v = $this->doubleValue) { - $payload['doubleValue'] = $v; + if (isset($input['longValue'])) { + return new FieldMemberLongValue(['longValue' => $input['longValue']]); } - if (null !== $v = $this->stringValue) { - $payload['stringValue'] = $v; + if (isset($input['doubleValue'])) { + return new FieldMemberDoubleValue(['doubleValue' => $input['doubleValue']]); } - if (null !== $v = $this->blobValue) { - $payload['blobValue'] = base64_encode($v); + if (isset($input['stringValue'])) { + return new FieldMemberStringValue(['stringValue' => $input['stringValue']]); } - if (null !== $v = $this->arrayValue) { - $payload['arrayValue'] = $v->requestBody(); + if (isset($input['blobValue'])) { + return new FieldMemberBlobValue(['blobValue' => $input['blobValue']]); + } + if (isset($input['arrayValue'])) { + return new FieldMemberArrayValue(['arrayValue' => $input['arrayValue']]); } - return $payload; + throw new InvalidArgument('Invalid union input'); } + + /** + * @internal + */ + abstract public function requestBody(): array; } diff --git a/src/Service/RdsDataService/src/ValueObject/FieldMemberArrayValue.php b/src/Service/RdsDataService/src/ValueObject/FieldMemberArrayValue.php new file mode 100644 index 000000000..e3d85fce0 --- /dev/null +++ b/src/Service/RdsDataService/src/ValueObject/FieldMemberArrayValue.php @@ -0,0 +1,50 @@ +arrayValue = isset($input['arrayValue']) ? ArrayValue::create($input['arrayValue']) : $this->throwException(new InvalidArgument('Missing required field "arrayValue".')); + } + + public function getArrayValue(): ArrayValue + { + return $this->arrayValue; + } + + /** + * @internal + */ + public function requestBody(): array + { + $payload = []; + $v = $this->arrayValue; + $payload['arrayValue'] = $v->requestBody(); + + return $payload; + } + + /** + * @return never + */ + private function throwException(\Throwable $exception) + { + throw $exception; + } +} diff --git a/src/Service/RdsDataService/src/ValueObject/FieldMemberBlobValue.php b/src/Service/RdsDataService/src/ValueObject/FieldMemberBlobValue.php new file mode 100644 index 000000000..29f06195d --- /dev/null +++ b/src/Service/RdsDataService/src/ValueObject/FieldMemberBlobValue.php @@ -0,0 +1,50 @@ +blobValue = $input['blobValue'] ?? $this->throwException(new InvalidArgument('Missing required field "blobValue".')); + } + + public function getBlobValue(): string + { + return $this->blobValue; + } + + /** + * @internal + */ + public function requestBody(): array + { + $payload = []; + $v = $this->blobValue; + $payload['blobValue'] = base64_encode($v); + + return $payload; + } + + /** + * @return never + */ + private function throwException(\Throwable $exception) + { + throw $exception; + } +} diff --git a/src/Service/RdsDataService/src/ValueObject/FieldMemberBooleanValue.php b/src/Service/RdsDataService/src/ValueObject/FieldMemberBooleanValue.php new file mode 100644 index 000000000..cd4dabff8 --- /dev/null +++ b/src/Service/RdsDataService/src/ValueObject/FieldMemberBooleanValue.php @@ -0,0 +1,50 @@ +booleanValue = $input['booleanValue'] ?? $this->throwException(new InvalidArgument('Missing required field "booleanValue".')); + } + + public function getBooleanValue(): bool + { + return $this->booleanValue; + } + + /** + * @internal + */ + public function requestBody(): array + { + $payload = []; + $v = $this->booleanValue; + $payload['booleanValue'] = (bool) $v; + + return $payload; + } + + /** + * @return never + */ + private function throwException(\Throwable $exception) + { + throw $exception; + } +} diff --git a/src/Service/RdsDataService/src/ValueObject/FieldMemberDoubleValue.php b/src/Service/RdsDataService/src/ValueObject/FieldMemberDoubleValue.php new file mode 100644 index 000000000..9e1f41351 --- /dev/null +++ b/src/Service/RdsDataService/src/ValueObject/FieldMemberDoubleValue.php @@ -0,0 +1,50 @@ +doubleValue = $input['doubleValue'] ?? $this->throwException(new InvalidArgument('Missing required field "doubleValue".')); + } + + public function getDoubleValue(): float + { + return $this->doubleValue; + } + + /** + * @internal + */ + public function requestBody(): array + { + $payload = []; + $v = $this->doubleValue; + $payload['doubleValue'] = $v; + + return $payload; + } + + /** + * @return never + */ + private function throwException(\Throwable $exception) + { + throw $exception; + } +} diff --git a/src/Service/RdsDataService/src/ValueObject/FieldMemberIsNull.php b/src/Service/RdsDataService/src/ValueObject/FieldMemberIsNull.php new file mode 100644 index 000000000..5cd89096d --- /dev/null +++ b/src/Service/RdsDataService/src/ValueObject/FieldMemberIsNull.php @@ -0,0 +1,50 @@ +isNull = $input['isNull'] ?? $this->throwException(new InvalidArgument('Missing required field "isNull".')); + } + + public function getIsNull(): bool + { + return $this->isNull; + } + + /** + * @internal + */ + public function requestBody(): array + { + $payload = []; + $v = $this->isNull; + $payload['isNull'] = (bool) $v; + + return $payload; + } + + /** + * @return never + */ + private function throwException(\Throwable $exception) + { + throw $exception; + } +} diff --git a/src/Service/RdsDataService/src/ValueObject/FieldMemberLongValue.php b/src/Service/RdsDataService/src/ValueObject/FieldMemberLongValue.php new file mode 100644 index 000000000..7c4e714c2 --- /dev/null +++ b/src/Service/RdsDataService/src/ValueObject/FieldMemberLongValue.php @@ -0,0 +1,50 @@ +longValue = $input['longValue'] ?? $this->throwException(new InvalidArgument('Missing required field "longValue".')); + } + + public function getLongValue(): int + { + return $this->longValue; + } + + /** + * @internal + */ + public function requestBody(): array + { + $payload = []; + $v = $this->longValue; + $payload['longValue'] = $v; + + return $payload; + } + + /** + * @return never + */ + private function throwException(\Throwable $exception) + { + throw $exception; + } +} diff --git a/src/Service/RdsDataService/src/ValueObject/FieldMemberStringValue.php b/src/Service/RdsDataService/src/ValueObject/FieldMemberStringValue.php new file mode 100644 index 000000000..6a6d8656f --- /dev/null +++ b/src/Service/RdsDataService/src/ValueObject/FieldMemberStringValue.php @@ -0,0 +1,50 @@ +stringValue = $input['stringValue'] ?? $this->throwException(new InvalidArgument('Missing required field "stringValue".')); + } + + public function getStringValue(): string + { + return $this->stringValue; + } + + /** + * @internal + */ + public function requestBody(): array + { + $payload = []; + $v = $this->stringValue; + $payload['stringValue'] = $v; + + return $payload; + } + + /** + * @return never + */ + private function throwException(\Throwable $exception) + { + throw $exception; + } +} diff --git a/src/Service/RdsDataService/src/ValueObject/FieldMemberUnknownToSdk.php b/src/Service/RdsDataService/src/ValueObject/FieldMemberUnknownToSdk.php new file mode 100644 index 000000000..ac89c7dd7 --- /dev/null +++ b/src/Service/RdsDataService/src/ValueObject/FieldMemberUnknownToSdk.php @@ -0,0 +1,39 @@ + + */ + private $unknown; + + /** + * @internal + * + * @param array $input + */ + public function __construct(array $input) + { + $this->unknown = $input; + } + + /** + * @return array + */ + public function getUnknown(): array + { + return $this->unknown; + } + + /** + * @internal + */ + public function requestBody(): array + { + throw new LogicException('request can not be generated for unknown object'); + } +} diff --git a/src/Service/S3Vectors/CHANGELOG.md b/src/Service/S3Vectors/CHANGELOG.md index 07a58561b..5c7ad3f76 100644 --- a/src/Service/S3Vectors/CHANGELOG.md +++ b/src/Service/S3Vectors/CHANGELOG.md @@ -2,6 +2,10 @@ ## NOT RELEASED +### BC-BREAK + +- Generic `VectorData` value object now have strict types for all its getters and setters. This may lead to type errors in existing code if you were passing or expecting different types. + ### Added - Add `UNKNOWN_TO_SDK` value to enums that is used when the API returns an value that is not (or not yet) known by the AsyncAws diff --git a/src/Service/S3Vectors/composer.json b/src/Service/S3Vectors/composer.json index 57b39a67a..a33c7373d 100644 --- a/src/Service/S3Vectors/composer.json +++ b/src/Service/S3Vectors/composer.json @@ -32,7 +32,7 @@ }, "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } } } diff --git a/src/Service/S3Vectors/src/Result/GetVectorsOutput.php b/src/Service/S3Vectors/src/Result/GetVectorsOutput.php index 90cefb8a7..9d893e499 100644 --- a/src/Service/S3Vectors/src/Result/GetVectorsOutput.php +++ b/src/Service/S3Vectors/src/Result/GetVectorsOutput.php @@ -6,6 +6,8 @@ use AsyncAws\Core\Result; use AsyncAws\S3Vectors\ValueObject\GetOutputVector; use AsyncAws\S3Vectors\ValueObject\VectorData; +use AsyncAws\S3Vectors\ValueObject\VectorDataMemberFloat32; +use AsyncAws\S3Vectors\ValueObject\VectorDataMemberUnknownToSdk; class GetVectorsOutput extends Result { @@ -73,8 +75,22 @@ private function populateResultGetVectorsOutputList(array $json): array private function populateResultVectorData(array $json): VectorData { - return new VectorData([ - 'float32' => !isset($json['float32']) ? null : $this->populateResultFloat32VectorData($json['float32']), + if (isset($json['float32'])) { + return $this->populateResultVectorDataMemberFloat32($json); + } + + return $this->populateResultVectorDataMemberUnknownToSdk($json); + } + + private function populateResultVectorDataMemberFloat32(array $json): VectorDataMemberFloat32 + { + return new VectorDataMemberFloat32([ + 'float32' => $this->populateResultFloat32VectorData($json['float32']), ]); } + + private function populateResultVectorDataMemberUnknownToSdk(array $json): VectorData + { + return new VectorDataMemberUnknownToSdk($json); + } } diff --git a/src/Service/S3Vectors/src/Result/ListVectorsOutput.php b/src/Service/S3Vectors/src/Result/ListVectorsOutput.php index 261352969..57697b8f8 100644 --- a/src/Service/S3Vectors/src/Result/ListVectorsOutput.php +++ b/src/Service/S3Vectors/src/Result/ListVectorsOutput.php @@ -9,6 +9,8 @@ use AsyncAws\S3Vectors\S3VectorsClient; use AsyncAws\S3Vectors\ValueObject\ListOutputVector; use AsyncAws\S3Vectors\ValueObject\VectorData; +use AsyncAws\S3Vectors\ValueObject\VectorDataMemberFloat32; +use AsyncAws\S3Vectors\ValueObject\VectorDataMemberUnknownToSdk; /** * @implements \IteratorAggregate @@ -138,8 +140,22 @@ private function populateResultListVectorsOutputList(array $json): array private function populateResultVectorData(array $json): VectorData { - return new VectorData([ - 'float32' => !isset($json['float32']) ? null : $this->populateResultFloat32VectorData($json['float32']), + if (isset($json['float32'])) { + return $this->populateResultVectorDataMemberFloat32($json); + } + + return $this->populateResultVectorDataMemberUnknownToSdk($json); + } + + private function populateResultVectorDataMemberFloat32(array $json): VectorDataMemberFloat32 + { + return new VectorDataMemberFloat32([ + 'float32' => $this->populateResultFloat32VectorData($json['float32']), ]); } + + private function populateResultVectorDataMemberUnknownToSdk(array $json): VectorData + { + return new VectorDataMemberUnknownToSdk($json); + } } diff --git a/src/Service/S3Vectors/src/ValueObject/VectorData.php b/src/Service/S3Vectors/src/ValueObject/VectorData.php index 873639d82..404030c4d 100644 --- a/src/Service/S3Vectors/src/ValueObject/VectorData.php +++ b/src/Service/S3Vectors/src/ValueObject/VectorData.php @@ -2,62 +2,34 @@ namespace AsyncAws\S3Vectors\ValueObject; +use AsyncAws\Core\Exception\InvalidArgument; + /** * The vector data in different formats. + * + * @psalm-inheritors VectorDataMemberFloat32|VectorDataMemberUnknownToSdk */ -final class VectorData +abstract class VectorData { - /** - * The vector data as 32-bit floating point numbers. The number of elements in this array must exactly match the - * dimension of the vector index where the operation is being performed. - * - * @var float[]|null - */ - private $float32; - - /** - * @param array{ - * float32?: float[]|null, - * } $input - */ - public function __construct(array $input) - { - $this->float32 = $input['float32'] ?? null; - } - /** * @param array{ - * float32?: float[]|null, + * float32: float[], * }|VectorData $input */ public static function create($input): self { - return $input instanceof self ? $input : new self($input); - } + if ($input instanceof self) { + return $input; + } + if (isset($input['float32'])) { + return new VectorDataMemberFloat32(['float32' => $input['float32']]); + } - /** - * @return float[] - */ - public function getFloat32(): array - { - return $this->float32 ?? []; + throw new InvalidArgument('Invalid union input'); } /** * @internal */ - public function requestBody(): array - { - $payload = []; - if (null !== $v = $this->float32) { - $index = -1; - $payload['float32'] = []; - foreach ($v as $listValue) { - ++$index; - $payload['float32'][$index] = $listValue; - } - } - - return $payload; - } + abstract public function requestBody(): array; } diff --git a/src/Service/S3Vectors/src/ValueObject/VectorDataMemberFloat32.php b/src/Service/S3Vectors/src/ValueObject/VectorDataMemberFloat32.php new file mode 100644 index 000000000..e56eda1b1 --- /dev/null +++ b/src/Service/S3Vectors/src/ValueObject/VectorDataMemberFloat32.php @@ -0,0 +1,60 @@ +float32 = $input['float32'] ?? $this->throwException(new InvalidArgument('Missing required field "float32".')); + } + + /** + * @return float[] + */ + public function getFloat32(): array + { + return $this->float32; + } + + /** + * @internal + */ + public function requestBody(): array + { + $payload = []; + $v = $this->float32; + + $index = -1; + $payload['float32'] = []; + foreach ($v as $listValue) { + ++$index; + $payload['float32'][$index] = $listValue; + } + + return $payload; + } + + /** + * @return never + */ + private function throwException(\Throwable $exception) + { + throw $exception; + } +} diff --git a/src/Service/S3Vectors/src/ValueObject/VectorDataMemberUnknownToSdk.php b/src/Service/S3Vectors/src/ValueObject/VectorDataMemberUnknownToSdk.php new file mode 100644 index 000000000..dfc97fc34 --- /dev/null +++ b/src/Service/S3Vectors/src/ValueObject/VectorDataMemberUnknownToSdk.php @@ -0,0 +1,39 @@ + + */ + private $unknown; + + /** + * @internal + * + * @param array $input + */ + public function __construct(array $input) + { + $this->unknown = $input; + } + + /** + * @return array + */ + public function getUnknown(): array + { + return $this->unknown; + } + + /** + * @internal + */ + public function requestBody(): array + { + throw new LogicException('request can not be generated for unknown object'); + } +} diff --git a/src/Service/S3Vectors/tests/Integration/S3VectorsClientTest.php b/src/Service/S3Vectors/tests/Integration/S3VectorsClientTest.php index 99e1fb63a..886deb843 100644 --- a/src/Service/S3Vectors/tests/Integration/S3VectorsClientTest.php +++ b/src/Service/S3Vectors/tests/Integration/S3VectorsClientTest.php @@ -22,6 +22,7 @@ use AsyncAws\S3Vectors\ValueObject\MetadataConfiguration; use AsyncAws\S3Vectors\ValueObject\PutInputVector; use AsyncAws\S3Vectors\ValueObject\VectorData; +use AsyncAws\S3Vectors\ValueObject\VectorDataMemberFloat32; class S3VectorsClientTest extends TestCase { @@ -278,7 +279,7 @@ public function testQueryVectors(): void 'indexName' => 'change me', 'indexArn' => 'change me', 'topK' => 1337, - 'queryVector' => new VectorData([ + 'queryVector' => new VectorDataMemberFloat32([ 'float32' => [1337], ]), 'filter' => 'change me', diff --git a/src/Service/S3Vectors/tests/Unit/Input/PutVectorsInputTest.php b/src/Service/S3Vectors/tests/Unit/Input/PutVectorsInputTest.php index 1f2c65367..80a5e64a8 100644 --- a/src/Service/S3Vectors/tests/Unit/Input/PutVectorsInputTest.php +++ b/src/Service/S3Vectors/tests/Unit/Input/PutVectorsInputTest.php @@ -5,7 +5,7 @@ use AsyncAws\Core\Test\TestCase; use AsyncAws\S3Vectors\Input\PutVectorsInput; use AsyncAws\S3Vectors\ValueObject\PutInputVector; -use AsyncAws\S3Vectors\ValueObject\VectorData; +use AsyncAws\S3Vectors\ValueObject\VectorDataMemberFloat32; class PutVectorsInputTest extends TestCase { @@ -17,7 +17,7 @@ public function testRequest(): void 'indexArn' => 'arn:aws:s3:us-east-1:123456789012:index/my-index', 'vectors' => [new PutInputVector([ 'key' => 'key1', - 'data' => new VectorData([ + 'data' => new VectorDataMemberFloat32([ 'float32' => [1.1, 2.2], ]), 'metadata' => ['tag' => 'value'], diff --git a/src/Service/S3Vectors/tests/Unit/Input/QueryVectorsInputTest.php b/src/Service/S3Vectors/tests/Unit/Input/QueryVectorsInputTest.php index 3b196280b..bc1826a88 100644 --- a/src/Service/S3Vectors/tests/Unit/Input/QueryVectorsInputTest.php +++ b/src/Service/S3Vectors/tests/Unit/Input/QueryVectorsInputTest.php @@ -4,7 +4,7 @@ use AsyncAws\Core\Test\TestCase; use AsyncAws\S3Vectors\Input\QueryVectorsInput; -use AsyncAws\S3Vectors\ValueObject\VectorData; +use AsyncAws\S3Vectors\ValueObject\VectorDataMemberFloat32; class QueryVectorsInputTest extends TestCase { @@ -15,7 +15,7 @@ public function testRequest(): void 'indexName' => 'my-index', 'indexArn' => 'arn:aws:s3:us-east-1:123456789012:index/my-index', 'topK' => 5, - 'queryVector' => new VectorData([ + 'queryVector' => new VectorDataMemberFloat32([ 'float32' => [1.23, 4.56], ]), 'filter' => ['tag' => 'value'], diff --git a/src/Service/S3Vectors/tests/Unit/S3VectorsClientTest.php b/src/Service/S3Vectors/tests/Unit/S3VectorsClientTest.php index a7b89b9b1..33ca5b2c0 100644 --- a/src/Service/S3Vectors/tests/Unit/S3VectorsClientTest.php +++ b/src/Service/S3Vectors/tests/Unit/S3VectorsClientTest.php @@ -32,7 +32,7 @@ use AsyncAws\S3Vectors\Result\QueryVectorsOutput; use AsyncAws\S3Vectors\S3VectorsClient; use AsyncAws\S3Vectors\ValueObject\PutInputVector; -use AsyncAws\S3Vectors\ValueObject\VectorData; +use AsyncAws\S3Vectors\ValueObject\VectorDataMemberFloat32; use Symfony\Component\HttpClient\MockHttpClient; class S3VectorsClientTest extends TestCase @@ -183,7 +183,8 @@ public function testPutVectors(): void $input = new PutVectorsInput([ 'vectors' => [new PutInputVector([ 'key' => 'change me', - 'data' => new VectorData([ + 'data' => new VectorDataMemberFloat32([ + 'float32' => [], ]), ])], ]); @@ -199,7 +200,8 @@ public function testQueryVectors(): void $input = new QueryVectorsInput([ 'topK' => 1337, - 'queryVector' => new VectorData([ + 'queryVector' => new VectorDataMemberFloat32([ + 'float32' => [], ]), ]); $result = $client->queryVectors($input);