Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dev/src/Command/ComponentNewCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
'distribution_name' => $new->composerPackage,
'release_level' => 'preview',
'client_documentation' => $documentationUrl,
'library_type' => 'GAPIC_AUTO',
'library_type' => str_ends_with($new->componentName, 'CommonProtos') ? 'CORE' : 'GAPIC_AUTO',
'api_shortname' => $new->shortName
];
$repoMetadataFullPath = $this->rootPath . '/.repo-metadata-full.json';
Expand Down
109 changes: 84 additions & 25 deletions dev/src/NewComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,30 @@ class NewComponent
public string $protoPath;
public ?string $version;

/**
* @param array<string, mixed> $options
*/
public static function fromProto(string $protoContents, string $protoFilename, array $options = []): self
{
$new = new self();
$isCommonProtos = self::isCommonProtos($protoContents);
$new->protoPackage = $options['proto-package']
?? self::extractPackageNameFromProtoContents($protoContents);
?? self::extractPackageNameFromProtoContents($protoContents, $isCommonProtos);
$new->phpNamespace = $options['php-namespace']
?? (self::extractPhpNamespaceFromProtoContents($protoContents)
?? (self::extractPhpNamespaceFromProtoContents($protoContents, $isCommonProtos)
?: self::derivePhpNamespaceFromProtoPackage($new->protoPackage));
$new->displayName = self::getDisplayName($new->phpNamespace);
$new->displayName = self::getDisplayName($new->phpNamespace, $isCommonProtos);
$new->componentName = $options['component-name']
?? self::getComponentName($new->displayName);
$new->composerPackage = self::getComposerPackageFromProtoPackage($new->protoPackage);
?? self::getComponentName($new->displayName, $isCommonProtos);
$new->composerPackage = self::getComposerPackage(
$new->protoPackage,
$new->phpNamespace,
$isCommonProtos
);
$new->githubRepo = self::getGithubRepo($new->composerPackage);
$new->gpbMetadataNamespace = self::getGpbMetadataNamespace($new->protoPackage);
$new->shortName = $options['api-short-name']
?? self::extractShortNameFromProtoContents($protoContents);
?? self::extractShortNameFromProtoContents($protoContents, $isCommonProtos);
$new->version = array_key_exists('api-version', $options)
? $options['api-version']
: self::extractVersionFromProtoFilename($protoFilename);
Expand All @@ -61,14 +69,21 @@ public static function fromProto(string $protoContents, string $protoFilename, a
return $new;
}

/**
* @param array<string, mixed> $options
*/
public static function fromOptions(array $options): self
{
$new = new self();
$new->protoPackage = $options['proto-package'] ?? '';
$new->phpNamespace = $options['php-namespace'] ?? '';
$new->displayName = self::getDisplayName($new->phpNamespace);
$new->componentName = $options['component-name'] ?? '';
$new->composerPackage = self::getComposerPackageFromProtoPackage($new->protoPackage);
$new->composerPackage = self::getComposerPackage(
$new->protoPackage,
$new->phpNamespace,
str_ends_with($new->componentName, 'CommonProtos')
);
$new->githubRepo = self::getGithubRepo($new->composerPackage);
$new->gpbMetadataNamespace = self::getGpbMetadataNamespace($new->protoPackage);
$new->shortName = $options['api-short-name'] ?? '';
Expand All @@ -88,23 +103,34 @@ public function getDocumentationUrl(): string

private static function getGithubRepo(string $composerPackage): string
{
if (str_starts_with($composerPackage, 'googleads/')) {
return 'googleapis/php-ads-' . substr($composerPackage, 10);
}
return 0 === strpos($composerPackage, 'google/cloud-')
? 'googleapis/google-cloud-php-' . substr($composerPackage, 13)
: 'googleapis/php-' . substr($composerPackage, 7);
}

private static function getComponentName(string $displayName): string
private static function getComponentName(string $displayName, bool $isCommonProtos = false): string
{
return trim(str_replace(['Google', 'Cloud', ' '], '', $displayName));
$componentName = trim(str_replace(['Google', 'Cloud', ' '], '', $displayName));
if ($isCommonProtos && !str_ends_with($componentName, 'CommonProtos')) {
$componentName .= 'CommonProtos';
}
return $componentName;
}

private static function getDisplayName(string $phpNamespace): string
private static function getDisplayName(string $phpNamespace, bool $isCommonProtos = false): string
{
$nameParts = explode('\\', $phpNamespace);
foreach ($nameParts as $i => $part) {
$nameParts[$i] = ltrim(strtolower(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', ' $0', $part)));
}
return ucwords(implode(' ', $nameParts));
$displayName = ucwords(implode(' ', $nameParts));
if ($isCommonProtos && !str_ends_with($displayName, 'Common Protos')) {
$displayName .= ' Common Protos';
}
return $displayName;
}

private static function getGpbMetadataNamespace(string $protoPackage): string
Expand All @@ -117,13 +143,28 @@ private static function derivePhpNamespaceFromProtoPackage(string $protoPackage)
return implode('\\', array_map('ucfirst', explode('.', $protoPackage)));
}

private static function getComposerPackageFromProtoPackage(string $protoPackage): string
{
return 'google/' . str_replace(
['google.', 'devtools.cloud', '.'],
['', 'cloud-', '-'],
private static function getComposerPackage(
string $protoPackage,
string $phpNamespace,
bool $isCommonProtos = false
): string {
$vendor = 'google';

if (str_starts_with($phpNamespace, 'Google\\Ads')) {
$vendor = 'googleads';
}

$name = str_replace(
['google.ads.', 'google.', 'devtools.cloud', '.'],
['', '', 'cloud-', '-'],
$protoPackage
);

if ($isCommonProtos && !str_ends_with($name, '-common-protos')) {
$name .= '-common-protos';
}

return $vendor . '/' . $name;
}

private static function getProtoPath(string $protoFilename, ?string $version): string
Expand All @@ -140,40 +181,58 @@ private static function getProtoPath(string $protoFilename, ?string $version): s
return implode('/', $parts);
}

private static function extractPackageNameFromProtoContents(string $protoContents): string
private static function isCommonProtos(string $protoContents): bool
{
// Any package which does not contain service clients is considered "Common Protos"
return !preg_match('/^\s*service\s+[A-Za-z0-9_]+/m', $protoContents);
Comment thread
bshaffer marked this conversation as resolved.
Resolved
}

private static function extractPackageNameFromProtoContents(
string $protoContents,
bool $isCommonProtos = false
): string {
if (!preg_match('/package (.*);/', $protoContents, $matches)) {
throw new RuntimeException('package name not found in proto file ');
}
$parts = explode('.', $matches[1]);
$version = array_pop($parts);
if ('v' !== $version[0]) {
if ('v' !== $version[0] && (!$isCommonProtos || !in_array($version, ['type', 'common']))) {
$parts[] = $version;
}
return implode('.', $parts);
}

private static function extractShortNameFromProtoContents(string $protoContents): string
{
private static function extractShortNameFromProtoContents(
string $protoContents,
bool $isCommonProtos = false
): string {
if (!preg_match(
'/option \(google.api.default_host\) =[\n\r\s]+"(.*).googleapis.com";/',
$protoContents,
$matches)
) {
$matches
)) {
if ($isCommonProtos) {
// common protos do not have an API shortname
return '';
Comment thread
bshaffer marked this conversation as resolved.
Resolved
}
throw new RuntimeException('short name not found in proto file');
}
return $matches[1];
}

private static function extractPhpNamespaceFromProtoContents(string $protoContents): ?string
{
private static function extractPhpNamespaceFromProtoContents(
string $protoContents,
bool $isCommonProtos = false
): ?string {
if (!preg_match('/option php_namespace = "(.*)";/', $protoContents, $matches)) {
return null;
}
// Remove version from namespace
$parts = explode('\\\\', $matches[1]);
$version = array_pop($parts);
if ('v' !== strtolower($version[0])) {
if ('v' !== strtolower($version[0])
&& (!$isCommonProtos || !in_array(strtolower($version), ['type', 'common']))
) {
$parts[] = $version;
}

Expand Down
51 changes: 50 additions & 1 deletion dev/tests/Unit/Command/ComponentNewCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;


/**
* @group dev
*/
Expand Down Expand Up @@ -417,6 +416,56 @@ public function testNewComponentWithPartialOptionsAndProtoPath()
$this->assertFileExists(self::$tmpDir . '/CustomSecretManagerName/README.md');
}

public function testNewComponentCommonProtosWithAllOptions()
{
$application = new Application();
$application->add(new ComponentNewCommand(self::$tmpDir));

$commandTester = new CommandTester($application->get('component:new'));

$commandTester->execute([
'--no-update' => true,
'--component-name' => 'GeoCommonProtos',
'--php-namespace' => 'Google\Geo',
'--proto-package' => 'google.geo',
'--api-short-name' => '',
'--api-version' => '',
'--product-docs' => 'https://cloud.google.com/geo/docs',
'--product-homepage' => 'https://cloud.google.com/geo',
]);

$repoMetadataFull = json_decode(file_get_contents(self::$tmpDir . '/.repo-metadata-full.json'), true);
$this->assertArrayHasKey('GeoCommonProtos', $repoMetadataFull);
$this->assertEquals('CORE', $repoMetadataFull['GeoCommonProtos']['library_type']);
$this->assertEquals('google/geo-common-protos', $repoMetadataFull['GeoCommonProtos']['distribution_name']);
$this->assertEquals('', $repoMetadataFull['GeoCommonProtos']['api_shortname']);
}

public function testNewComponentAdsWithAllOptions()
{
$application = new Application();
$application->add(new ComponentNewCommand(self::$tmpDir));

$commandTester = new CommandTester($application->get('component:new'));

$commandTester->execute([
'--no-update' => true,
'--component-name' => 'AdsAdManager',
'--php-namespace' => 'Google\Ads\AdManager\V1',
'--proto-package' => 'google.ads.admanager',
'--api-short-name' => 'admanager',
'--api-version' => 'v1',
'--product-docs' => 'https://developers.google.com/ad-manager/api',
'--product-homepage' => 'https://developers.google.com/ad-manager',
]);

$repoMetadataFull = json_decode(file_get_contents(self::$tmpDir . '/.repo-metadata-full.json'), true);
$this->assertArrayHasKey('AdsAdManager', $repoMetadataFull);
$this->assertEquals('GAPIC_AUTO', $repoMetadataFull['AdsAdManager']['library_type']);
$this->assertEquals('googleads/admanager', $repoMetadataFull['AdsAdManager']['distribution_name']);
$this->assertEquals('admanager', $repoMetadataFull['AdsAdManager']['api_shortname']);
}

private function assertComposerJson(string $componentName)
{
$composerPath = sprintf('%s/../../fixtures/component/%s/composer.json', __DIR__, $componentName);
Expand Down
Loading
Loading