diff --git a/composer.json b/composer.json index d2abd1385a82..36de436b07e0 100644 --- a/composer.json +++ b/composer.json @@ -114,4 +114,4 @@ "entry": "ServiceBuilder.php" } } -} \ No newline at end of file +} diff --git a/src/BigQuery/BigQueryClient.php b/src/BigQuery/BigQueryClient.php index dc22e59402e8..e89c24e0c1a4 100644 --- a/src/BigQuery/BigQueryClient.php +++ b/src/BigQuery/BigQueryClient.php @@ -20,7 +20,6 @@ use Google\Cloud\BigQuery\Connection\ConnectionInterface; use Google\Cloud\BigQuery\Connection\Rest; use Google\Cloud\BigQuery\Job; -use Google\Cloud\Core\ArrayTrait; use Google\Cloud\Core\ClientTrait; use Google\Cloud\Core\ExponentialBackoff; use Google\Cloud\Core\Int64; @@ -43,7 +42,6 @@ */ class BigQueryClient { - use ArrayTrait; use ClientTrait; use JobConfigurationTrait; @@ -254,6 +252,8 @@ public function runQuery($query, array $options = []) * If not provided default settings will be used, with the exception * of `configuration.query.useLegacySql`, which defaults to `false` * in this client. + * @type string $jobIdPrefix If given, the returned job ID will be of + * format `{$jobIdPrefix-}{jobId}`. **Defaults to** `null`. * } * @return Job */ diff --git a/src/BigQuery/JobConfigurationTrait.php b/src/BigQuery/JobConfigurationTrait.php index 2a3829453b55..2ec37ca0be99 100644 --- a/src/BigQuery/JobConfigurationTrait.php +++ b/src/BigQuery/JobConfigurationTrait.php @@ -17,11 +17,16 @@ namespace Google\Cloud\BigQuery; +use Google\Cloud\Core\ArrayTrait; +use Ramsey\Uuid\Uuid; + /** * A trait used to build out configuration for jobs. */ trait JobConfigurationTrait { + use ArrayTrait; + /** * Builds a configuration for a job. * @@ -33,6 +38,8 @@ trait JobConfigurationTrait */ public function buildJobConfig($name, $projectId, array $config, array $userDefinedOptions) { + $jobIdPrefix = $this->pluck('jobIdPrefix', $userDefinedOptions, false); + if (isset($userDefinedOptions['jobConfig'])) { $config = $userDefinedOptions['jobConfig'] + $config; } @@ -41,9 +48,31 @@ public function buildJobConfig($name, $projectId, array $config, array $userDefi return [ 'projectId' => $projectId, + 'jobReference' => [ + 'projectId' => $projectId, + 'jobId' => $this->generateJobId($jobIdPrefix) + ], 'configuration' => [ $name => $config ] ] + $userDefinedOptions; } + + /** + * Generate a Job ID with an optional user-defined prefix. + * + * @param string $jobIdPrefix [optional] If given, the returned job ID will + * be of format `{$jobIdPrefix-}{jobId}`. **Defaults to** `null`. + * @return string + */ + protected function generateJobId($jobIdPrefix = null) + { + $jobId = ''; + + if ($jobIdPrefix) { + $jobId = $jobIdPrefix . '-'; + } + + return $jobId . Uuid::uuid4(); + } } diff --git a/src/BigQuery/Table.php b/src/BigQuery/Table.php index 12e8c7de2687..53fd95d6ad43 100644 --- a/src/BigQuery/Table.php +++ b/src/BigQuery/Table.php @@ -18,7 +18,6 @@ namespace Google\Cloud\BigQuery; use Google\Cloud\BigQuery\Connection\ConnectionInterface; -use Google\Cloud\Core\ArrayTrait; use Google\Cloud\Core\ConcurrencyControlTrait; use Google\Cloud\Core\Exception\NotFoundException; use Google\Cloud\Core\Iterator\ItemIterator; @@ -33,7 +32,6 @@ */ class Table { - use ArrayTrait; use ConcurrencyControlTrait; use JobConfigurationTrait; @@ -239,6 +237,8 @@ function (array $row) use ($schema) { * @type array $jobConfig Configuration settings for a copy job are * outlined in the [API Docs for `configuration.copy`](https://goo.gl/m8dro9). * If not provided default settings will be used. + * @type string $jobIdPrefix If given, the returned job ID will be of + * format `{$jobIdPrefix-}{jobId}`. **Defaults to** `null`. * } * @return Job */ @@ -287,6 +287,8 @@ public function copy(Table $destination, array $options = []) * @type array $jobConfig Configuration settings for an extract job are * outlined in the [API Docs for `configuration.extract`](https://goo.gl/SQ9XAE). * If not provided default settings will be used. + * @type string $jobIdPrefix If given, the returned job ID will be of + * format `{$jobIdPrefix-}{jobId}`. **Defaults to** `null`. * } * @return Job */ @@ -334,6 +336,8 @@ public function export($destination, array $options = []) * @type array $jobConfig Configuration settings for a load job are * outlined in the [API Docs for `configuration.load`](https://goo.gl/j6jyHv). * If not provided default settings will be used. + * @type string $jobIdPrefix If given, the returned job ID will be of + * format `{$jobIdPrefix-}{jobId}`. **Defaults to** `null`. * } * @return Job */ diff --git a/src/BigQuery/composer.json b/src/BigQuery/composer.json index 6eded48fdc12..cf54b78f6b6b 100644 --- a/src/BigQuery/composer.json +++ b/src/BigQuery/composer.json @@ -4,7 +4,8 @@ "license": "Apache-2.0", "minimum-stability": "stable", "require": { - "google/cloud-core": "^1.0" + "google/cloud-core": "^1.0", + "ramsey/uuid": "~3" }, "suggest": { "google/cloud-storage": "Makes it easier to load data from Cloud Storage into BigQuery" diff --git a/tests/snippets/BigQuery/BigQueryClientTest.php b/tests/snippets/BigQuery/BigQueryClientTest.php index e89986a6272f..d4aef7c69583 100644 --- a/tests/snippets/BigQuery/BigQueryClientTest.php +++ b/tests/snippets/BigQuery/BigQueryClientTest.php @@ -36,6 +36,8 @@ */ class BigQueryClientTest extends SnippetTestCase { + const JOBID = 'myJobId'; + private $connection; private $client; private $result = [ @@ -63,7 +65,7 @@ class BigQueryClientTest extends SnippetTestCase public function setUp() { $this->connection = $this->prophesize(ConnectionInterface::class); - $this->client = \Google\Cloud\Dev\stub(BigQueryClient::class); + $this->client = \Google\Cloud\Dev\stub(BigQueryTestClient::class); $this->client->___setProperty('connection', $this->connection->reveal()); } @@ -106,6 +108,7 @@ public function testRunQueryWithNamedParameters() $this->connection ->insertJob([ 'projectId' => 'my-awesome-project', + 'jobReference' => ['projectId' => 'my-awesome-project', 'jobId' => self::JOBID], 'configuration' => [ 'query' => [ 'parameterMode' => 'named', @@ -159,6 +162,7 @@ public function testRunQueryWithPositionalParameters() $this->connection ->insertJob([ 'projectId' => 'my-awesome-project', + 'jobReference' => ['projectId' => 'my-awesome-project', 'jobId' => self::JOBID], 'configuration' => [ 'query' => [ 'parameterMode' => 'positional', @@ -351,3 +355,11 @@ public function testTimestamp() $this->assertInstanceOf(Timestamp::class, $res->returnVal()); } } + +class BigQueryTestClient extends BigQueryClient +{ + protected function generateJobId($jobIdPrefix = null) + { + return $jobIdPrefix ? $jobIdPrefix . '-' . BigQueryClientTest::JOBID : BigQueryClientTest::JOBID; + } +} diff --git a/tests/unit/BigQuery/BigQueryClientTest.php b/tests/unit/BigQuery/BigQueryClientTest.php index 95bb39d4c7c6..186dcb65e140 100644 --- a/tests/unit/BigQuery/BigQueryClientTest.php +++ b/tests/unit/BigQuery/BigQueryClientTest.php @@ -34,8 +34,9 @@ */ class BigQueryClientTest extends \PHPUnit_Framework_TestCase { + const JOBID = 'myJobId'; + public $connection; - public $jobId = 'myJobId'; public $projectId = 'myProjectId'; public $datasetId = 'myDatasetId'; public $client; @@ -43,7 +44,7 @@ class BigQueryClientTest extends \PHPUnit_Framework_TestCase public function setUp() { $this->connection = $this->prophesize(ConnectionInterface::class); - $this->client = new BigQueryTestClient(['projectId' => $this->projectId]); + $this->client = \Google\Cloud\Dev\stub(BigQueryTestClient::class, ['options' => ['projectId' => $this->projectId]]); } /** @@ -57,25 +58,26 @@ public function testRunsQuery($query, $options, $expected) 'projectId' => $projectId, 'configuration' => [ 'query' => $expected - ] + ], + 'jobReference' => ['projectId' => $projectId, 'jobId' => self::JOBID] ]) ->willReturn([ - 'jobReference' => ['jobId' => $this->jobId] + 'jobReference' => ['jobId' => self::JOBID] ]) ->shouldBeCalledTimes(1); $this->connection->getQueryResults(Argument::any()) ->willReturn([ 'jobReference' => [ - 'jobId' => $this->jobId + 'jobId' => self::JOBID ], 'jobComplete' => true ]) ->shouldBeCalledTimes(1); - $this->client->setConnection($this->connection->reveal()); + $this->client->___setProperty('connection', $this->connection->reveal()); $queryResults = $this->client->runQuery($query, $options); $this->assertInstanceOf(QueryResults::class, $queryResults); - $this->assertEquals($this->jobId, $queryResults->identity()['jobId']); + $this->assertEquals(self::JOBID, $queryResults->identity()['jobId']); } /** @@ -89,26 +91,30 @@ public function testRunsQueryWithRetry($query, $options, $expected) 'projectId' => $projectId, 'configuration' => [ 'query' => $expected - ] + ], + 'jobReference' => ['projectId' => $projectId, 'jobId' => self::JOBID] ]) ->willReturn([ - 'jobReference' => ['jobId' => $this->jobId] + 'jobReference' => [ + 'jobId' => self::JOBID + ], + 'jobComplete' => false ]) ->shouldBeCalledTimes(1); $this->connection->getQueryResults(Argument::any()) ->willReturn([ 'jobReference' => [ - 'jobId' => $this->jobId + 'jobId' => self::JOBID ], 'jobComplete' => true ]) ->shouldBeCalledTimes(1); - $this->client->setConnection($this->connection->reveal()); + $this->client->___setProperty('connection', $this->connection->reveal()); $queryResults = $this->client->runQuery($query, $options); $this->assertInstanceOf(QueryResults::class, $queryResults); - $this->assertEquals($this->jobId, $queryResults->identity()['jobId']); + $this->assertEquals(self::JOBID, $queryResults->identity()['jobId']); } /** @@ -122,17 +128,22 @@ public function testRunsQueryAsJob($query, $options, $expected) 'projectId' => $projectId, 'configuration' => [ 'query' => $expected + ], + 'jobReference' => [ + 'projectId' => $projectId, + 'jobId' => self::JOBID ] ]) ->willReturn([ - 'jobReference' => ['jobId' => $this->jobId] + 'jobReference' => ['jobId' => self::JOBID] ]) ->shouldBeCalledTimes(1); - $this->client->setConnection($this->connection->reveal()); + + $this->client->___setProperty('connection', $this->connection->reveal()); $job = $this->client->runQueryAsJob($query, $options); $this->assertInstanceOf(Job::class, $job); - $this->assertEquals($this->jobId, $job->id()); + $this->assertEquals(self::JOBID, $job->id()); } public function queryDataProvider() @@ -209,8 +220,8 @@ public function queryDataProvider() public function testGetsJob() { - $this->client->setConnection($this->connection->reveal()); - $this->assertInstanceOf(Job::class, $this->client->job($this->jobId)); + $this->client->___setProperty('connection', $this->connection->reveal()); + $this->assertInstanceOf(Job::class, $this->client->job(self::JOBID)); } public function testGetsJobsWithNoResults() @@ -219,7 +230,7 @@ public function testGetsJobsWithNoResults() ->willReturn([]) ->shouldBeCalledTimes(1); - $this->client->setConnection($this->connection->reveal()); + $this->client->___setProperty('connection', $this->connection->reveal()); $jobs = iterator_to_array($this->client->jobs()); $this->assertEmpty($jobs); @@ -230,15 +241,15 @@ public function testGetsJobsWithoutToken() $this->connection->listJobs(['projectId' => $this->projectId]) ->willReturn([ 'jobs' => [ - ['jobReference' => ['jobId' => $this->jobId]] + ['jobReference' => ['jobId' => self::JOBID]] ] ]) ->shouldBeCalledTimes(1); - $this->client->setConnection($this->connection->reveal()); + $this->client->___setProperty('connection', $this->connection->reveal()); $jobs = iterator_to_array($this->client->jobs()); - $this->assertEquals($this->jobId, $jobs[0]->id()); + $this->assertEquals(self::JOBID, $jobs[0]->id()); } public function testGetsJobsWithToken() @@ -257,19 +268,19 @@ public function testGetsJobsWithToken() ]) ->willReturn([ 'jobs' => [ - ['jobReference' => ['jobId' => $this->jobId]] + ['jobReference' => ['jobId' => self::JOBID]] ] ])->shouldBeCalledTimes(1); - $this->client->setConnection($this->connection->reveal()); + $this->client->___setProperty('connection', $this->connection->reveal()); $job = iterator_to_array($this->client->jobs()); - $this->assertEquals($this->jobId, $job[1]->id()); + $this->assertEquals(self::JOBID, $job[1]->id()); } public function testGetsDataset() { - $this->client->setConnection($this->connection->reveal()); + $this->client->___setProperty('connection', $this->connection->reveal()); $this->assertInstanceOf(Dataset::class, $this->client->dataset($this->datasetId)); } @@ -279,7 +290,7 @@ public function testGetsDatasetsWithNoResults() ->willReturn([]) ->shouldBeCalledTimes(1); - $this->client->setConnection($this->connection->reveal()); + $this->client->___setProperty('connection', $this->connection->reveal()); $datasets = iterator_to_array($this->client->datasets()); $this->assertEmpty($datasets); @@ -295,7 +306,7 @@ public function testGetsDatasetsWithoutToken() ]) ->shouldBeCalledTimes(1); - $this->client->setConnection($this->connection->reveal()); + $this->client->___setProperty('connection', $this->connection->reveal()); $datasets = iterator_to_array($this->client->datasets()); $this->assertEquals($this->datasetId, $datasets[0]->id()); @@ -319,7 +330,7 @@ public function testGetsDatasetsWithToken() ) ->shouldBeCalledTimes(2); - $this->client->setConnection($this->connection->reveal()); + $this->client->___setProperty('connection', $this->connection->reveal()); $dataset = iterator_to_array($this->client->datasets()); $this->assertEquals($this->datasetId, $dataset[1]->id()); @@ -334,7 +345,7 @@ public function testCreatesDataset() ] ]) ->shouldBeCalledTimes(1); - $this->client->setConnection($this->connection->reveal()); + $this->client->___setProperty('connection', $this->connection->reveal()); $dataset = $this->client->createDataset($this->datasetId, [ 'metadata' => [ @@ -376,8 +387,8 @@ public function testGetsTimestamp() class BigQueryTestClient extends BigQueryClient { - public function setConnection($connection) + protected function generateJobId($jobIdPrefix = null) { - $this->connection = $connection; + return $jobIdPrefix ? $jobIdPrefix . '-' . BigQueryClientTest::JOBID : BigQueryClientTest::JOBID; } } diff --git a/tests/unit/BigQuery/JobConfigurationTraitTest.php b/tests/unit/BigQuery/JobConfigurationTraitTest.php new file mode 100644 index 000000000000..c834ad1929f8 --- /dev/null +++ b/tests/unit/BigQuery/JobConfigurationTraitTest.php @@ -0,0 +1,46 @@ +trait = \Google\Cloud\Dev\impl(JobConfigurationTrait::class); + } + + public function testGenerateJobId() + { + $uuid = $this->trait->call('generateJobId'); + $this->assertTrue(is_string($uuid)); + $this->assertTrue(Uuid::isValid($uuid)); + } + + public function testGenerateJobIdWithPrefix() + { + $this->assertTrue(strpos($this->trait->call('generateJobId', ['foobar']), 'foobar-') === 0); + } +} diff --git a/tests/unit/BigQuery/TableTest.php b/tests/unit/BigQuery/TableTest.php index 783c5d65dfd5..46110adb5dd7 100644 --- a/tests/unit/BigQuery/TableTest.php +++ b/tests/unit/BigQuery/TableTest.php @@ -33,6 +33,8 @@ */ class TableTest extends \PHPUnit_Framework_TestCase { + const JOBID = 'myJobId'; + public $connection; public $storageConnection; public $mapper; @@ -58,7 +60,7 @@ class TableTest extends \PHPUnit_Framework_TestCase ]; public $insertJobResponse = [ 'jobReference' => [ - 'jobId' => 'myJobId' + 'jobId' => self::JOBID ] ]; @@ -80,7 +82,7 @@ public function getObject() public function getTable($connection, array $data = [], $tableId = null) { - return new Table( + return new TableStub( $connection->reveal(), $tableId ?: $this->tableId, $this->datasetId, @@ -221,6 +223,10 @@ public function testRunsCopyJob() 'projectId' => $this->projectId ] ] + ], + 'jobReference' => [ + 'projectId' => $this->projectId, + 'jobId' => self::JOBID ] ]; $this->connection->insertJob(Argument::exact($expectedArguments)) @@ -251,6 +257,10 @@ public function testRunsExportJob($destinationObject) 'projectId' => $this->projectId ] ] + ], + 'jobReference' => [ + 'projectId' => $this->projectId, + 'jobId' => self::JOBID ] ]; $this->connection->insertJob(Argument::exact($expectedArguments)) @@ -295,6 +305,10 @@ public function testRunsLoadJob() 'projectId' => $this->projectId ] ] + ], + 'jobReference' => [ + 'projectId' => $this->projectId, + 'jobId' => self::JOBID ] ]; $this->connection->insertJobUpload(Argument::exact($expectedArguments)) @@ -323,6 +337,10 @@ public function testRunsLoadJobFromStorage() 'projectId' => $this->projectId ] ] + ], + 'jobReference' => [ + 'projectId' => $this->projectId, + 'jobId' => self::JOBID ] ]; $this->connection->insertJob(Argument::exact($expectedArguments)) @@ -439,3 +457,11 @@ public function testGetsIdentity() $this->assertEquals($this->projectId, $table->identity()['projectId']); } } + +class TableStub extends Table +{ + protected function generateJobId($jobIdPrefix = null) + { + return $jobIdPrefix ? $jobIdPrefix . '-' . BigQueryClientTest::JOBID : BigQueryClientTest::JOBID; + } +}