diff --git a/build/docs/classes/CodeSnippetGenerator.php b/build/docs/classes/CodeSnippetGenerator.php new file mode 100644 index 0000000000..5096b8d3e2 --- /dev/null +++ b/build/docs/classes/CodeSnippetGenerator.php @@ -0,0 +1,114 @@ +service = $service; + } + + public function __invoke($operation, $params, $comments, $isInput = true) + { + $messageShape = $isInput + ? $this->service->getOperation($operation)->getInput() + : $this->service->getOperation($operation)->getOutput(); + $code = $this->visit($messageShape, $params, '', [], $comments); + + return $isInput + ? "\$result = \$client->" . lcfirst($operation) . "($code);" + : $code; + } + + private function visit(Shape $shape, $value, $indent, $path, $comments) + { + switch ($shape['type']) { + case 'structure': + return $this->structure($shape, $value, $indent, $path, $comments); + case 'list': + return $this->arr($shape, $value, $indent, $path, $comments); + case 'map': + return $this->map($shape, $value, $indent, $path, $comments); + case 'string': + return "'{$value}'"; + case 'timestamp': + return ""; + case 'blob': + return ""; + default: + return $value; + } + } + + private function structure(StructureShape $shape, $value, $indent, $path, $comments) + { + $lines = ['[']; + foreach ($value as $key => $val) { + $path[] = ".{$key}"; + $comment = $this->getCommentFor($path, $comments); + $shapeVal = $this->visit($shape->getMember($key), $val, "{$indent} ", $path, $comments); + $lines[] = rtrim("{$indent} '{$key}' => {$shapeVal}, {$comment}"); + array_pop($path); + } + $lines[] = "{$indent}]"; + + return implode("\n", $lines); + } + + private function arr(ListShape $shape, $value, $indent, $path, $comments) + { + $lines = ['[']; + foreach ($value as $ind => $val) { + $path[] = "[{ind}]"; + $comment = $this->getCommentFor($path, $comments); + $shapeVal = $this->visit($shape->getMember(), $val, "{$indent} ", $path, $comments); + $lines[] = rtrim("{$indent} {$shapeVal}, {$comment}"); + array_pop($path); + } + $lines[] = "{$indent}]"; + + return implode("\n", $lines); + } + + private function map(MapShape $shape, $value, $indent, $path, $comments) + { + $lines = ['[']; + foreach ($value as $key => $val) { + $path[] = ".{$key}"; + $comment = $this->getCommentFor($path, $comments); + $shapeVal = $this->visit($shape->getValue(), $val, "{$indent} ", $path, $comments); + $lines[] = rtrim("{$indent} '{$key}' => {$shapeVal}, {$comment}"); + array_pop($path); + } + $lines[] = "{$indent}]"; + + return implode("\n", $lines); + } + + private function getCommentFor($path, $comments) + { + $key = preg_replace('/^\./', '', implode('', $path)); + if (isset($comments[$key])) { + return '// ' . $comments[$key]; + } else { + return ''; + } + } + + private function isAssociative(array $arr) + { + return $arr = array_values($arr); + } +} diff --git a/build/docs/classes/DocsBuilder.php b/build/docs/classes/DocsBuilder.php index 5e44e89eff..06cbe1f793 100644 --- a/build/docs/classes/DocsBuilder.php +++ b/build/docs/classes/DocsBuilder.php @@ -85,7 +85,8 @@ public function build() $aliases[$service->title][$version] []= $alias; continue; } - $this->renderService($service); + $examples = $this->loadExamples($name, $version); + $this->renderService($service, $examples); $services[$service->title][$version] = $service; } } @@ -166,7 +167,7 @@ private function updateQuickLinks(array $services) $this->replaceInner('index', $quickLinks, ':quickLinks:'); } - private function renderService(Service $service) + private function renderService(Service $service, $examples) { $html = new HtmlDocument; $html->open('div', 'page-header'); @@ -207,7 +208,12 @@ private function renderService(Service $service) $html->section(2, 'Operations'); foreach ($service->api->getOperations() as $opName => $operation) { - $html->append($this->createHtmlForOperation($service, $opName, $operation)); + $html->append($this->createHtmlForOperation( + $service, + $opName, + $operation, + isset($examples[$opName]) ? $examples[$opName] : [] + )); } $html->section(2, 'Shapes'); @@ -276,6 +282,16 @@ private function gatherServiceVersions() return json_decode(file_get_contents($manifest), true); } + private function loadExamples($name, $version) + { + $path = __DIR__ . "/../../../src/data/{$name}/{$version}/examples-1.json"; + try { + return \Aws\load_compiled_json($path)['examples']; + } catch (\InvalidArgumentException $e) { + return []; + } + } + private function updateClients(array $services) { fwrite(STDOUT, "Updating client pages with service links\n"); @@ -581,7 +597,7 @@ private function createHtmlForPaginators(HtmlDocument $html, Api $service) $html->close(); } - private function createHtmlForOperation(Service $service, $name, Operation $operation) + private function createHtmlForOperation(Service $service, $name, Operation $operation, $examples) { $html = new HtmlDocument; $html->open('div', 'operation-container'); @@ -656,6 +672,29 @@ private function createHtmlForOperation(Service $service, $name, Operation $oper $html->close(); } + // Examples + if (!empty($examples)) { + $generator = new CodeSnippetGenerator($service->api); + $html->elem('h4', null, 'Examples'); + foreach ($examples as $number => $example) { + $exampleNumber = $number + 1; + $exampleId = $this->exampleSlug($name, $exampleNumber); + $html->open('h5', ['id' => $exampleId]); + $html->elem('span', null, 'Example ' . $exampleNumber . ': ' . $example['title']); + $html->elem('a', ['href' => '#' . $exampleId], $html->glyph('link')); + $html->close(); + $html->elem('p', null, $example['description']); + $comments = $example['comments']; + $input = $generator($name, $example['input'], $comments['input']); + $html->elem('pre', null, $input->getCode($example['input'], $name, $comments['input'])); + if (isset($example['output'])) { + $html->elem('p', null, 'Result syntax:'); + $output = $generator($name, $example['output'], $comments['output'], false); + $html->elem('pre', null, $output->getCode($example['output'], $name, $comments['output'], false)); + } + } + } + $html->close(); // operation-container return $html; @@ -765,6 +804,11 @@ private function getPrimitivePhpType($type) } } + private function exampleSlug($name, $number) + { + return strtolower($name) . '-example-' . $number; + } + private function memberSlug($name) { return 'shape-' . strtolower($name); diff --git a/build/docs/theme/resources/css/style.css b/build/docs/theme/resources/css/style.css index 45974afaad..239fe5dfd2 100644 --- a/build/docs/theme/resources/css/style.css +++ b/build/docs/theme/resources/css/style.css @@ -622,7 +622,7 @@ ul.methods-summary { color: #AE1E1E; } -.shape-container h5 { +.operation-container h5, .shape-container h5 { color: #c60; font-weight: bold; margin: 24px 0; diff --git a/tests/Build/Docs/CodeSnippetGeneratorTest.php b/tests/Build/Docs/CodeSnippetGeneratorTest.php new file mode 100644 index 0000000000..b08516a2a7 --- /dev/null +++ b/tests/Build/Docs/CodeSnippetGeneratorTest.php @@ -0,0 +1,144 @@ +assertSame($expected, $builder($operation, $input, [], $isInput)); + } + + public function exampleProvider() + { + $provider = ApiProvider::defaultProvider(); + return [ + // strings in input + [ + new Service($provider->resolve($provider, 'api', 'sqs', 'latest'), $provider), + 'GetQueueUrl', + [ + 'QueueName' => 'MyQueue', + 'QueueOwnerAWSAccountId' => '12345678910', + ], + <<<'EOCS' +$result = $client->getQueueUrl([ + 'QueueName' => 'MyQueue', + 'QueueOwnerAWSAccountId' => '12345678910', +]); +EOCS + ], + // strings in output + [ + new Service($provider->resolve($provider, 'api', 'sqs', 'latest'), $provider), + 'GetQueueUrl', + ["QueueUrl" => "https://queue.amazonaws.com/123456789101112/MyQueue"], + <<<'EOCS' +[ + 'QueueUrl' => 'https://queue.amazonaws.com/123456789101112/MyQueue', +] +EOCS + , false, + ], + // Dates and numbers in input + [ + new Service($provider->resolve($provider, 'api', 'autoscaling', 'latest'), $provider), + 'DescribeScheduledActions', + [ + 'EndTime' => 1449799223, + 'MaxRecords' => 10, + 'StartTime' => 1449798223, + ], + <<<'EOCS' +$result = $client->describeScheduledActions([ + 'EndTime' => , + 'MaxRecords' => 10, + 'StartTime' => , +]); +EOCS + ], + // Dates, numbers, and lists in output + [ + new Service($provider->resolve($provider, 'api', 'autoscaling', 'latest'), $provider), + 'DescribeScheduledActions', + [ + 'NextToken' => 'Next', + 'ScheduledUpdateGroupActions' => [ + [ + 'AutoScalingGroupName' => 'my-autoscaling-group', + 'DesiredCapacity' => 4, + 'EndTime' => 1449799223, + 'MaxSize' => 5, + 'MinSize' => 3, + 'Recurrence' => 'daily', + 'ScheduledActionARN' => 'arn:aws:iam::123456789012:user/David', + 'ScheduledActionName' => 'David', + 'StartTime' => 1449798223, + 'Time' => 1449798223, + ], + ], + ], + <<<'EOCS' +[ + 'NextToken' => 'Next', + 'ScheduledUpdateGroupActions' => [ + [ + 'AutoScalingGroupName' => 'my-autoscaling-group', + 'DesiredCapacity' => 4, + 'EndTime' => , + 'MaxSize' => 5, + 'MinSize' => 3, + 'Recurrence' => 'daily', + 'ScheduledActionARN' => 'arn:aws:iam::123456789012:user/David', + 'ScheduledActionName' => 'David', + 'StartTime' => , + 'Time' => , + ], + ], +] +EOCS + , false, + ], + // Blobs and maps in input + [ + new Service($provider->resolve($provider, 'api', 'dynamodb', 'latest'), $provider), + 'PutItem', + [ + 'Item' => [ + 'Key' => [ + 'B' => "3q2+7w==", + ], + ], + ], + <<<'EOCS' +$result = $client->putItem([ + 'Item' => [ + 'Key' => [ + 'B' => , + ], + ], +]); +EOCS + ] + ]; + } +} diff --git a/tests/CommandTest.php b/tests/CommandTest.php index 21c7edcfbc..4a38068140 100644 --- a/tests/CommandTest.php +++ b/tests/CommandTest.php @@ -1,5 +1,5 @@