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!: [Datastore] Upgrade to PHP V2 #7179

Open
wants to merge 45 commits into
base: main
Choose a base branch
from

Conversation

yash30201
Copy link
Contributor

@yash30201 yash30201 commented Mar 26, 2024

Main PR for all the Datastore PHP v2 upgrade changes as mentioned below:

  • Enable direct Gapic Client instantiation in Google\Cloud\Datastore\DatastoreClient 7215b2e
  • Add credentials wrapper support in DatastoreClient 237531f
  • Instantiate serializer and Request Handler in DatastoreClient. cac76f1
  • Update Resource classes to add serializer and requesthandler in their constructor. f39189f
  • Update all the Snippet and Units tests to add $requestHandler prophecy and serializer be2533c
  • Update Client + ResourceClass methods to use request handler for everything, correspondingly, update their unit tests and snippet tests.
  • Remove all references of connection classes from everywhere 4c32fc9
  • Update System tests [Not needed as none of the tests broke🎉]
  • Add MIGRATING.md file e098d66
  • Update DatastoreClient's documentation 2196aa2
  • Remove old GAPIC files (class aliases, etc) 0596f27
  • Increase minimum core to ^1.55 bf2c50b
  • Mark Operation class as internal d6e2823

BREAKING_CHANGE_REASON=RC-1 for the version 2 of the Datastore library.

@yash30201 yash30201 requested review from a team as code owners April 5, 2024 10:40
Datastore/src/DatastoreClient.php Show resolved Hide resolved
Datastore/src/DatastoreClient.php Outdated Show resolved Hide resolved
Datastore/src/EntityMapper.php Show resolved Hide resolved
Datastore/src/Operation.php Outdated Show resolved Hide resolved
Datastore/src/Operation.php Outdated Show resolved Hide resolved
Datastore/src/Operation.php Outdated Show resolved Hide resolved
Datastore/src/Operation.php Outdated Show resolved Hide resolved
Datastore/src/Operation.php Outdated Show resolved Hide resolved
Datastore/src/Operation.php Outdated Show resolved Hide resolved
Datastore/src/Operation.php Outdated Show resolved Hide resolved
chore(Datastore): Enhance readability of RunQuery and RunAggregationQuery APIs
@bshaffer
Copy link
Contributor

bshaffer commented May 7, 2024

I'll give this a more thorough review later today, but don't forget we need to remove all V1 GAPIC client classes. For Datastore V1, that would be Datastore/src/V1/DatastoreClient.php and Datastore/src/V1/DatastoreGrpcClient.php.

Also the PR title should be renamed to better describe what the PR is doing. For PubSub, we said "remove connection classes". I don't mind using V2 in the title (since we are bumping to a V2 version), but we do not need to include PHP in the title.

Copy link
Contributor

@bshaffer bshaffer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! A few suggestions with testing and some other minor things. The bigger question I have is with the Operations class - it looks like we are doing a LOT of converting of parameters. Why is so much of this necessary? Since we are doing a major version break, we have an opportunity to clean this up. Parameters which need to be changed should fall in two categories:

  1. We think the current (non-standard) interface is better. This would include things like accepting the string values of constants instead of their int versions. And since we know which parameters are enums, we could even automate this for all parameters (something to consider, but maybe not a good idea)
  2. We think the GAPIC interface is better. These should then be deprecated and either removed entirely (if they are not heavily used parameters) or renamed/converted to their new parameter/type.

* Non zero positive integer $x => `shouldBeCalledTimes($x)`
* ]
*/
private function mockSendRequest($methodName, $params, $returnValue, $shouldBeCalledTimes = null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not a fan of abstracting out the test logic to the trait like this - it makes the tests harder to read. You may end up saving a few lines of code, but you sacrifice readability. If I have to maintain these tests later, I may have to make changes to your abstracted functions, and take time to learn them, and even fix bugs in them.

I'd rather see the tests mock requests to the requestHandler directly. See below for an example.

);

if (!is_null($shouldBeCalledTimes)) {
if ($shouldBeCalledTimes == 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specifying $shouldBeCalledTimes as 0 to mean shouldBeCalled() is intuitive, especially since shouldNotBeCalled() exists.

Also, generally speaking, we should be able to specify the exact number of times we expect the function to be called (e.g. using shouldBeCalledOnce() and shouldBeCalledTimes() instead of just shouldBeCalled()).

Comment on lines +101 to +106
$this->mockSendRequest(
'commit',
[],
['mutationResults' => [['version' => 1]]],
0
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following reads cleaner than using an abstraction function, and accomplishes the same thing

$this->requestHandler->sendRequest(
    DatastoreGapicClient::class,
    'commit',
    Argument::type(CommitRequest::class),
    []
)
    ->shouldBeCalled()
    ->willReturn(['mutationResults' => [['version' => 1]]]);

]);
$this->mockSendRequest(
'beginTransaction',
[],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the vast majority of these are providing an empty second argument, which means this can simply check the Request class instead of the more complicated logic in mockSendRequest (e.g. what I have above)

'beginTransaction',
[],
['transaction' => 'foo'],
0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've noticed we are using 0 here almost always, but since we expect the call to only happen once, this should be 1, which is a tighter assertion than simply ensuring the method is called.

So when these are refactored out of that function (which I think they should be), you would want to use shouldBeCalledOnce() instead of shouldBeCalled()

Comment on lines +979 to +987
'project_id' => isset($key['partitionId']['projectId'])
? $key['partitionId']['projectId']
: null,
'namespace_id' => isset($key['partitionId']['namespaceId'])
? $key['partitionId']['namespaceId']
: null,
'database_id' => isset($key['partitionId']['databaseId'])
? $key['partitionId']['databaseId']
: null,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the null coalescing operator for readability

Suggested change
'project_id' => isset($key['partitionId']['projectId'])
? $key['partitionId']['projectId']
: null,
'namespace_id' => isset($key['partitionId']['namespaceId'])
? $key['partitionId']['namespaceId']
: null,
'database_id' => isset($key['partitionId']['databaseId'])
? $key['partitionId']['databaseId']
: null,
'project_id' => $key['partitionId']['projectId'] ?? null,
'namespace_id' => $key['partitionId']['namespaceId'] ?? null,
'database_id' => $key['partitionId']['databaseId'] ?? null,

$local = [];

if (isset($key['partitionId'])) {
$p = $this->arrayFilterRemoveNull([
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit but it's more readable to just use short functions:

$p = array_filter($array, fn ($v) => !is_null($v));

}

return $filter;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this logic be in a Serializer class? This makes me a bit nervous, because changes to these parameters will break the API...

Is there a generic way we can do this logic? For instance, Message::serializeToJsonString will handle string enums (e.g. "AND" and "OR")

Comment on lines +1079 to +1080
// verify that the operator, given as enum value, exists.
PropertyFilterOperator::name($operator);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we really need to do this? The API will validate this.

$filter['compositeFilter']['op'] = CompositeFilterOperator::PBOR;
} else {
$filter['compositeFilter']['op'] = CompositeFilterOperator::OPERATOR_UNSPECIFIED;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we not doing the same thing here as above and just call CompositeFilterOperator::value()? The way we have it here, if the "op" is anything but AND or OR, it gets overwritten by OPERATOR_UNSPECIFIED.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants