Skip to content

Commit

Permalink
Merge pull request aws#837 from jeskew/feature/generate-shared-examples
Browse files Browse the repository at this point in the history
Feature/generate shared examples
  • Loading branch information
jeskew committed Dec 14, 2015
2 parents 9fee106 + 856d8c5 commit 7539470
Show file tree
Hide file tree
Showing 9 changed files with 312 additions and 10 deletions.
114 changes: 114 additions & 0 deletions build/docs/classes/CodeSnippetGenerator.php
@@ -0,0 +1,114 @@
<?php
namespace Aws\Build\Docs;

use Aws\Api\ListShape;
use Aws\Api\MapShape;
use Aws\Api\Service;
use Aws\Api\Shape;
use Aws\Api\StructureShape;

/**
* @internal
*/
class CodeSnippetGenerator
{
/** @var Service */
private $service;

public function __construct(Service $service)
{
$this->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 "<DateTimeInterface>";
case 'blob':
return "<binary string>";
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);
}
}
52 changes: 48 additions & 4 deletions build/docs/classes/DocsBuilder.php
Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion build/docs/theme/resources/css/style.css
Expand Up @@ -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;
Expand Down
144 changes: 144 additions & 0 deletions tests/Build/Docs/CodeSnippetGeneratorTest.php
@@ -0,0 +1,144 @@
<?php
namespace Aws\Test\Build\Docs;


use Aws\Api\ApiProvider;
use Aws\Api\Service;
use Aws\Build\Docs\CodeSnippetGenerator;

class CodeSnippetGeneratorTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider exampleProvider
*
* @param Service $service
* @param string $operation
* @param array $input
* @param string $expected
* @param bool $isInput
*/
public function testCanBuildCodeExamples(
Service $service,
$operation,
array $input,
$expected,
$isInput = true
) {
$builder = new CodeSnippetGenerator($service);
$this->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' => <DateTimeInterface>,
'MaxRecords' => 10,
'StartTime' => <DateTimeInterface>,
]);
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' => <DateTimeInterface>,
'MaxSize' => 5,
'MinSize' => 3,
'Recurrence' => 'daily',
'ScheduledActionARN' => 'arn:aws:iam::123456789012:user/David',
'ScheduledActionName' => 'David',
'StartTime' => <DateTimeInterface>,
'Time' => <DateTimeInterface>,
],
],
]
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' => <binary string>,
],
],
]);
EOCS
]
];
}
}
2 changes: 1 addition & 1 deletion tests/CommandTest.php
@@ -1,5 +1,5 @@
<?php
namespace Aws\Tests;
namespace Aws\Test;

use Aws\Command;
use Aws\HandlerList;
Expand Down
2 changes: 1 addition & 1 deletion tests/DynamoDb/MarshalerTest.php
@@ -1,5 +1,5 @@
<?php
namespace Aws\Tests\DynamoDb;
namespace Aws\Test\DynamoDb;

use Aws\DynamoDb\Marshaler;
use Aws\DynamoDb\BinaryValue;
Expand Down
2 changes: 1 addition & 1 deletion tests/S3/SSECMiddlewareTest.php
@@ -1,5 +1,5 @@
<?php
namespace Aws\Tests\S3;
namespace Aws\Test\S3;

use Aws\Middleware;
use Aws\Result;
Expand Down
2 changes: 1 addition & 1 deletion tests/S3/TransferTest.php
@@ -1,5 +1,5 @@
<?php
namespace Aws\Tests\S3;
namespace Aws\Test\S3;

use Aws\CommandInterface;
use Aws\Result;
Expand Down

0 comments on commit 7539470

Please sign in to comment.