Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for grpc only gapic #707

Merged
merged 6 commits into from
Apr 25, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"Testing\\BasicBidiStreaming\\": "tests/Unit/ProtoTests/BasicBidiStreaming/out/src",
"Testing\\BasicClientStreaming\\": "tests/Unit/ProtoTests/BasicClientStreaming/out/src",
"Testing\\BasicDiregapic\\": "tests/Unit/ProtoTests/BasicDiregapic/out/src",
"Testing\\BasicGrpcOnly\\": "tests/Unit/ProtoTests/BasicGrpcOnly/out/src",
"Testing\\BasicLro\\": "tests/Unit/ProtoTests/BasicLro/out/src",
"Testing\\BasicOneof\\": "tests/Unit/ProtoTests/BasicOneof/out/src",
"Testing\\BasicOneofNew\\": "tests/Unit/ProtoTests/BasicOneofNew/out/src",
Expand Down
10 changes: 5 additions & 5 deletions rules_php_gapic/php_gapic.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def php_gapic_srcjar(
rest_numeric_enums = False,
generate_snippets = True,
# Supported values validated and specified in src/Utils/MigrationMode.php.
migration_mode = "PRE_MIGRATION_SURFACE_ONLY",
migration_mode = "PRE_MIGRATION_SURFACE_ONLY",
generator_binary = Label("//rules_php_gapic:php_gapic_generator_binary"),
**kwargs):
plugin_file_args = {}
Expand All @@ -69,10 +69,10 @@ def php_gapic_srcjar(
# Transport.
if transport == None:
transport = "grpc+rest"
if transport == "grpc":
fail("Error: gRPC-only PHP GAPIC libraries are not yet supported")
if transport != "grpc+rest" and transport != "rest":
fail("Error: Only 'grpc+rest' or 'rest' transports are supported")
if transport != "grpc+rest" and transport != "rest" and transport != "grpc":
fail("Error: Only 'grpc+rest', 'rest' or `grpc` transports are supported")
if transport == "grpc" and migration_mode != "NEW_SURFACE_ONLY":
fail("Error: 'grpc' transport is only supported with 'NEW_SURFACE_ONLY' migration mode")

# Set plugin arguments.
plugin_args = ["metadata"] # Generate the gapic_metadata.json file.
Expand Down
10 changes: 7 additions & 3 deletions src/CodeGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -404,10 +404,14 @@ private static function generateServices(
$code = ResourcesGenerator::generateDescriptorConfig($service, $gapicYamlConfig);
$code = Formatter::format($code);
yield ["src/{$version}resources/{$service->descriptorConfigFilename}", $code];

// Resource: rest_client_config.php
$code = ResourcesGenerator::generateRestConfig($service, $serviceYamlConfig, $numericEnums);
$code = Formatter::format($code);
yield ["src/{$version}resources/{$service->restConfigFilename}", $code];
if ($service->transportType !== Transport::GRPC) {
$code = ResourcesGenerator::generateRestConfig($service, $serviceYamlConfig, $numericEnums);
$code = Formatter::format($code);
yield ["src/{$version}resources/{$service->restConfigFilename}", $code];
}

// Resource: client_config.json
$json = ResourcesGenerator::generateClientConfig($service, $gapicYamlConfig, $grpcServiceConfig);
yield ["src/{$version}resources/{$service->clientConfigFilename}", $json];
Expand Down
91 changes: 60 additions & 31 deletions src/Generation/GapicClientV2Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ private function getClientDefaults(): PhpClassMember

// TODO: Consolidate setting all the known array values together.
// We do this here to maintain the existing sensible ordering.
if ($this->serviceDetails->transportType === Transport::GRPC_REST) {
if ($this->serviceDetails->transportType !== Transport::REST) {
$clientDefaultValues['gcpApiConfigPath'] =
AST::concat(AST::__DIR__, "/../resources/{$this->serviceDetails->grpcConfigFilename}");
}
Expand All @@ -444,11 +444,15 @@ private function getClientDefaults(): PhpClassMember
$credentialsConfig['useJwtAccessWithScope'] = false;
}
$clientDefaultValues['credentialsConfig'] = AST::array($credentialsConfig);
$clientDefaultValues['transportConfig'] = AST::array([
'rest' => AST::array([
'restClientConfigPath' => AST::concat(AST::__DIR__, "/../resources/{$this->serviceDetails->restConfigFilename}"),
])
]);

if ($this->serviceDetails->transportType !== Transport::GRPC) {
$clientDefaultValues['transportConfig'] = AST::array([
'rest' => AST::array([
'restClientConfigPath' => AST::concat(AST::__DIR__, "/../resources/{$this->serviceDetails->restConfigFilename}"),
])
]);
}

if ($this->serviceDetails->hasCustomOp) {
$clientDefaultValues['operationsClientClass'] = AST::access(
$this->ctx->type($this->serviceDetails->customOperationServiceClientType),
Expand Down Expand Up @@ -478,15 +482,23 @@ private function defaultTransport()

private function supportedTransports()
{
if ($this->serviceDetails->transportType !== Transport::REST) {
Hectorhammett marked this conversation as resolved.
Show resolved Hide resolved
return null;
if ($this->serviceDetails->transportType === Transport::REST) {
return AST::method('supportedTransports')
->withPhpDocText('Implements ClientOptionsTrait::supportedTransports.')
->withAccess(Access::PRIVATE, Access::STATIC)
->withBody(AST::block(
AST::return(AST::array(['rest']))
));
}

if ($this->serviceDetails->transportType === Transport::GRPC) {
return AST::method('supportedTransports')
->withPhpDocText('Implements ClientOptionsTrait::supportedTransports.')
->withAccess(Access::PRIVATE, Access::STATIC)
->withBody(AST::block(
AST::return(AST::array(['grpc', 'grpc-fallback']))
));
}
return AST::method('supportedTransports')
->withPhpDocText('Implements GapicClientTrait::supportedTransports.')
->withAccess(Access::PRIVATE, Access::STATIC)
->withBody(AST::block(
AST::return(AST::array(['rest']))
));
}

private function construct(): PhpClassMember
Expand All @@ -498,30 +510,30 @@ private function construct(): PhpClassMember
$options = AST::var('options');
$optionsParam = AST::param(ResolvedType::array(), $options, AST::array([]));
$clientOptions = AST::var('clientOptions');
$transportType = $this->serviceDetails->transportType;

// Assumes there are only two transport types.
$isGrpcRest = $this->serviceDetails->transportType === Transport::GRPC_REST;

$restTransportDocText = 'At the moment, supports only `rest`.';
$grpcTransportDocText = 'May be either the string `rest` or `grpc`. Defaults to `grpc` if gRPC support is detected on the system.';
$transportDocText =
PhpDoc::text(
'The transport used for executing network requests. ',
$isGrpcRest ? $grpcTransportDocText : $restTransportDocText,
$this->transportDocText($transportType),
'*Advanced usage*: Additionally, it is possible to pass in an already instantiated',
// TODO(vNext): Don't use a fully-qualified type here.
$ctx->type(Type::fromName(TransportInterface::class), true),
'object. Note that when this object is provided, any settings in $transportConfig, and any $apiEndpoint',
'setting, will be ignored.'
);

$transportConfigSampleValues = [
'grpc' => AST::arrayEllipsis(),
'rest' => AST::arrayEllipsis()
];

$transportConfigSampleValues = [];
if ($isGrpcRest) {
$transportConfigSampleValues['grpc'] = AST::arrayEllipsis();
if (Transport::isGrpcOnly($transportType)) {
unset($transportConfigSampleValues['rest']);
} elseif (Transport::isRestOnly($transportType)) {
unset($transportConfigSampleValues['grpc']);
}
// Set this value here, don't initialize it, so we can maintain alphabetical order
// for the resulting printed doc.
$transportConfigSampleValues['rest'] = AST::arrayEllipsis();

$transportConfigDocText =
PhpDoc::text(
'Configuration options that will be used to construct the transport. Options for',
Expand All @@ -539,22 +551,26 @@ private function construct(): PhpClassMember
'See the',
AST::call(
$ctx->type(
Type::fromName($isGrpcRest ? GrpcTransport::class : RestTransport::class),
Type::fromName(
Transport::isRestOnly($transportType) ?
RestTransport::class :
GrpcTransport::class
),
true
),
AST::method('build')
)(),
$isGrpcRest ? 'and' : '',
$isGrpcRest
? AST::call(
Transport::isGrpcRest($transportType) ? 'and' : '',
Transport::isGrpcRest($transportType) ?
AST::call(
$ctx->type(
Type::fromName(RestTransport::class),
true
),
AST::method('build')
)()
: '',
$isGrpcRest ? 'methods ' : 'method ',
Transport::isGrpcRest($transportType) ? 'methods ' : 'method ',
'for the supported options.'
);
return AST::method('__construct')
Expand Down Expand Up @@ -661,6 +677,19 @@ private function construct(): PhpClassMember
));
}

private function transportDocText(int $transportType): string
{
if (Transport::isRestOnly($transportType)) {
return 'At the moment, supports only `rest`.';
}

if (Transport::isGrpcOnly($transportType)) {
return 'At the moment, supports only `grpc`.';
}

return 'May be either the string `rest` or `grpc`. Defaults to `grpc` if gRPC support is detected on the system.';
}

private function rpcMethod(MethodDetails $method): PhpClassMember
{
$request = AST::var('request');
Expand Down
23 changes: 22 additions & 1 deletion src/Utils/Transport.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class Transport
// supported in the future (e.g. gRPC only).
public const GRPC_REST = 1;
public const REST = 2;
public const GRPC = 3;

/**
* Returns true if the given transport string indicates that grpc+rest transports
Expand All @@ -38,9 +39,29 @@ public static function parseTransport(?string $transport): int
return static::REST;
}
if ($transport === "grpc") {
throw new \Exception("gRPC-only PHP clients are not supported at this time");
return static::GRPC;
}

throw new \Exception("Transport $transport not supported");
}

public static function isRestOnly(int $transport): bool
{
return Transport::compareTransports(Transport::REST, $transport);
}

public static function isGrpcOnly(int $transport): bool
{
return Transport::compareTransports(Transport::GRPC, $transport);
}

public static function isGrpcRest(int $transport): bool
{
return Transport::compareTransports(Transport::GRPC_REST, $transport);
}

private static function compareTransports(int $transportA, int $transportB): bool
{
return $transportA === $transportB;
}
bshaffer marked this conversation as resolved.
Show resolved Hide resolved
}
3 changes: 2 additions & 1 deletion tests/Integration/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,8 @@ php_gapic_library(
gapic_yaml = "apis/redis/v1/redis_gapic.yaml",
grpc_service_config = "@com_google_googleapis//google/cloud/redis/v1:redis_grpc_service_config.json",
service_yaml = "@com_google_googleapis//google/cloud/redis/v1:redis_v1.yaml",
migration_mode = "MIGRATION_MODE_UNSPECIFIED",
migration_mode = "NEW_SURFACE_ONLY",
transport = "grpc",
deps = [
":redis_php_grpc",
":redis_php_proto",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
// [START redis_v1_generated_CloudRedis_CreateInstance_sync]
use Google\ApiCore\ApiException;
use Google\ApiCore\OperationResponse;
use Google\Cloud\Redis\V1\CloudRedisClient;
use Google\Cloud\Redis\V1\Client\CloudRedisClient;
use Google\Cloud\Redis\V1\CreateInstanceRequest;
use Google\Cloud\Redis\V1\Instance;
use Google\Cloud\Redis\V1\Instance\Tier;
use Google\Rpc\Status;
Expand Down Expand Up @@ -80,16 +81,20 @@ function create_instance_sample(
// Create a client.
$cloudRedisClient = new CloudRedisClient();

// Prepare any non-scalar elements to be passed along with the request.
// Prepare the request message.
$instance = (new Instance())
->setName($instanceName)
->setTier($instanceTier)
->setMemorySizeGb($instanceMemorySizeGb);
$request = (new CreateInstanceRequest())
->setParent($formattedParent)
->setInstanceId($instanceId)
->setInstance($instance);

// Call the API and handle any network failures.
try {
/** @var OperationResponse $response */
$response = $cloudRedisClient->createInstance($formattedParent, $instanceId, $instance);
$response = $cloudRedisClient->createInstance($request);
$response->pollUntilComplete();

if ($response->operationSucceeded()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
// [START redis_v1_generated_CloudRedis_DeleteInstance_sync]
use Google\ApiCore\ApiException;
use Google\ApiCore\OperationResponse;
use Google\Cloud\Redis\V1\CloudRedisClient;
use Google\Cloud\Redis\V1\Client\CloudRedisClient;
use Google\Cloud\Redis\V1\DeleteInstanceRequest;
use Google\Rpc\Status;

/**
Expand All @@ -42,10 +43,14 @@ function delete_instance_sample(string $formattedName): void
// Create a client.
$cloudRedisClient = new CloudRedisClient();

// Prepare the request message.
$request = (new DeleteInstanceRequest())
->setName($formattedName);

// Call the API and handle any network failures.
try {
/** @var OperationResponse $response */
$response = $cloudRedisClient->deleteInstance($formattedName);
$response = $cloudRedisClient->deleteInstance($request);
$response->pollUntilComplete();

if ($response->operationSucceeded()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
// [START redis_v1_generated_CloudRedis_ExportInstance_sync]
use Google\ApiCore\ApiException;
use Google\ApiCore\OperationResponse;
use Google\Cloud\Redis\V1\CloudRedisClient;
use Google\Cloud\Redis\V1\Client\CloudRedisClient;
use Google\Cloud\Redis\V1\ExportInstanceRequest;
use Google\Cloud\Redis\V1\Instance;
use Google\Cloud\Redis\V1\OutputConfig;
use Google\Rpc\Status;
Expand All @@ -47,13 +48,16 @@ function export_instance_sample(string $name): void
// Create a client.
$cloudRedisClient = new CloudRedisClient();

// Prepare any non-scalar elements to be passed along with the request.
// Prepare the request message.
$outputConfig = new OutputConfig();
$request = (new ExportInstanceRequest())
->setName($name)
->setOutputConfig($outputConfig);

// Call the API and handle any network failures.
try {
/** @var OperationResponse $response */
$response = $cloudRedisClient->exportInstance($name, $outputConfig);
$response = $cloudRedisClient->exportInstance($request);
$response->pollUntilComplete();

if ($response->operationSucceeded()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
// [START redis_v1_generated_CloudRedis_FailoverInstance_sync]
use Google\ApiCore\ApiException;
use Google\ApiCore\OperationResponse;
use Google\Cloud\Redis\V1\CloudRedisClient;
use Google\Cloud\Redis\V1\Client\CloudRedisClient;
use Google\Cloud\Redis\V1\FailoverInstanceRequest;
use Google\Cloud\Redis\V1\Instance;
use Google\Rpc\Status;

Expand All @@ -43,10 +44,14 @@ function failover_instance_sample(string $formattedName): void
// Create a client.
$cloudRedisClient = new CloudRedisClient();

// Prepare the request message.
$request = (new FailoverInstanceRequest())
->setName($formattedName);

// Call the API and handle any network failures.
try {
/** @var OperationResponse $response */
$response = $cloudRedisClient->failoverInstance($formattedName);
$response = $cloudRedisClient->failoverInstance($request);
$response->pollUntilComplete();

if ($response->operationSucceeded()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@

// [START redis_v1_generated_CloudRedis_GetInstance_sync]
use Google\ApiCore\ApiException;
use Google\Cloud\Redis\V1\CloudRedisClient;
use Google\Cloud\Redis\V1\Client\CloudRedisClient;
use Google\Cloud\Redis\V1\GetInstanceRequest;
use Google\Cloud\Redis\V1\Instance;

/**
Expand All @@ -40,10 +41,14 @@ function get_instance_sample(string $formattedName): void
// Create a client.
$cloudRedisClient = new CloudRedisClient();

// Prepare the request message.
$request = (new GetInstanceRequest())
->setName($formattedName);

// Call the API and handle any network failures.
try {
/** @var Instance $response */
$response = $cloudRedisClient->getInstance($formattedName);
$response = $cloudRedisClient->getInstance($request);
printf('Response data: %s' . PHP_EOL, $response->serializeToJsonString());
} catch (ApiException $ex) {
printf('Call failed with message: %s' . PHP_EOL, $ex->getMessage());
Expand Down