Skip to content

Commit 6a1293a

Browse files
authored
👽 Fix Google Cloud Storage adapter (#664)
1 parent a805ed6 commit 6a1293a

File tree

7 files changed

+122
-56
lines changed

7 files changed

+122
-56
lines changed

‎.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ phpunit.xml
1010
tests/adapters/*
1111
!tests/adapters/*.dist
1212
vendor/
13+
tests/Gaufrette/Functional/adapters/*.php

‎CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11

2+
v0.11.0
3+
=======
4+
5+
## Added
6+
7+
- Google Cloud Storage: options to automatically create bucket if not exists
8+
29
v0.10.0
310
=======
411

‎Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ docker.all-deps: docker.deps ## Install dependencies
2424
docker/run-task php${PHP_VERSION} composer require --no-update \
2525
aws/aws-sdk-php:^3.158 \
2626
rackspace/php-opencloud:^1.9.2 \
27-
google/apiclient:^1.1.3 \
27+
google/apiclient:^2.12 \
2828
doctrine/dbal:^2.3 \
2929
league/flysystem:^1.0 \
3030
microsoft/azure-storage-blob:^1.0 \

‎doc/adapters/google-cloud-storage.md

+24-17
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ currentMenu: google-cloud-storage
66

77
To use the GoogleCloudStorage adapter you will need to create a connection using the [Google APIs Client Library for PHP]
88
(https://github.com/google/google-api-php-client) and create a Client ID/Service Account in your [Developers Console]
9-
(https://console.developers.google.com/). You can then create the `\Google_Service_Storage` which is required for the
9+
(https://console.developers.google.com/). You can then create the `\Google\Service\Storage` which is required for the
1010
GoogleCloudStorage adapter.
1111

1212
## Example
@@ -17,23 +17,30 @@ GoogleCloudStorage adapter.
1717
use Gaufrette\Filesystem;
1818
use Gaufrette\Adapter\GoogleCloudStorage;
1919

20-
$client = new \Google_Client();
21-
$client->setClientId('xxxxxxxxxxxxxxx.apps.googleusercontent.com');
22-
$client->setApplicationName('Gaufrette');
20+
$keyFileLocation = '/home/me/path/to/service-auth-key.json';
21+
$bucketName = 'gaufrette-bucket-test-' . uniqid();
22+
$projectId = 'your-project-id-000';
23+
$bucketLocation = 'EUROPE-WEST9';
24+
25+
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $keyFileLocation);
2326

24-
$cred = new \Google_Auth_AssertionCredentials(
25-
26-
array(\Google_Service_Storage::DEVSTORAGE_FULL_CONTROL),
27-
file_get_contents('key.p12')
27+
$client = new \Google\Client();
28+
$client->setApplicationName('Gaufrette');
29+
$client->addScope(Google\Service\Storage::DEVSTORAGE_FULL_CONTROL);
30+
$client->useApplicationDefaultCredentials();
31+
32+
$service = new \Google\Service\Storage($client);
33+
$adapter = new GoogleCloudStorage(
34+
$service,
35+
$bucketName,
36+
[
37+
// Options to set for automatic creation of the bucket
38+
GoogleCloudStorage::OPTION_CREATE_BUCKET_IF_NOT_EXISTS => true,
39+
GoogleCloudStorage::OPTION_PROJECT_ID => $projectId,
40+
GoogleCloudStorage::OPTION_LOCATION => $bucketLocation,
41+
],
42+
true
2843
);
29-
$client->setAssertionCredentials($cred);
30-
if ($client->getAuth()->isAccessTokenExpired()) {
31-
$client->getAuth()->refreshTokenWithAssertion($cred);
32-
}
33-
34-
$service = new \Google_Service_Storage($client);
35-
$adapter = new Gaufrette\Adapter\GoogleCloudStorage($service, $config['gcsBucket'], array(
36-
'acl' => 'public',
37-
), true);
44+
3845
$filesystem = new Gaufrette\Filesystem($adapter);
3946
```

‎src/Gaufrette/Adapter/GoogleCloudStorage.php

+63-17
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
namespace Gaufrette\Adapter;
44

55
use Gaufrette\Adapter;
6+
use Google\Service\Storage;
7+
use Google\Service\Storage\Bucket;
8+
use Google\Service\Storage\StorageObject;
9+
use Google\Service\Exception as ServiceException;
10+
use Google\Service\Storage\BucketIamConfiguration;
11+
use Google\Service\Storage\BucketIamConfigurationUniformBucketLevelAccess;
612
use GuzzleHttp;
713

814
/**
@@ -12,37 +18,44 @@
1218
*/
1319
class GoogleCloudStorage implements Adapter, MetadataSupporter, ListKeysAware
1420
{
21+
public const OPTION_CREATE_BUCKET_IF_NOT_EXISTS = 'create';
22+
public const OPTION_PROJECT_ID = 'project_id';
23+
public const OPTION_LOCATION = 'bucket_location';
24+
public const OPTION_STORAGE_CLASS = 'storage_class';
25+
1526
protected $service;
1627
protected $bucket;
17-
protected $options;
28+
protected $options = [
29+
self::OPTION_CREATE_BUCKET_IF_NOT_EXISTS => false,
30+
self::OPTION_STORAGE_CLASS => 'STANDARD',
31+
'directory' => '',
32+
'acl' => 'private',
33+
];
1834
protected $bucketExists;
1935
protected $metadata = [];
2036
protected $detectContentType;
2137

2238
/**
23-
* @param \Google_Service_Storage $service The storage service class with authenticated
39+
* @param Storage $service The storage service class with authenticated
2440
* client and full access scope
2541
* @param string $bucket The bucket name
2642
* @param array $options Options can be directory and acl
2743
* @param bool $detectContentType Whether to detect the content type or not
2844
*/
2945
public function __construct(
30-
\Google_Service_Storage $service,
46+
Storage $service,
3147
$bucket,
3248
array $options = [],
3349
$detectContentType = false
3450
) {
35-
if (!class_exists(\Google_Service_Storage::class)) {
51+
if (!class_exists(Storage::class)) {
3652
throw new \LogicException('You need to install package "google/apiclient" to use this adapter');
3753
}
3854

3955
$this->service = $service;
4056
$this->bucket = $bucket;
4157
$this->options = array_replace(
42-
[
43-
'directory' => '',
44-
'acl' => 'private',
45-
],
58+
$this->options,
4659
$options
4760
);
4861

@@ -80,6 +93,7 @@ public function getBucket()
8093
*/
8194
public function setBucket($bucket)
8295
{
96+
$this->bucketExists = null;
8397
$this->bucket = $bucket;
8498
}
8599

@@ -145,7 +159,7 @@ public function write($key, $content)
145159
unset($metadata['ContentType']);
146160
}
147161

148-
$object = new \Google_Service_Storage_StorageObject();
162+
$object = new StorageObject();
149163
$object->name = $path;
150164

151165
if (isset($metadata['ContentDisposition'])) {
@@ -182,7 +196,7 @@ public function write($key, $content)
182196
}
183197

184198
return $object->getSize();
185-
} catch (\Google_Service_Exception $e) {
199+
} catch (ServiceException $e) {
186200
return false;
187201
}
188202
}
@@ -197,7 +211,7 @@ public function exists($key)
197211

198212
try {
199213
$this->service->objects->get($this->bucket, $path);
200-
} catch (\Google_Service_Exception $e) {
214+
} catch (ServiceException $e) {
201215
return false;
202216
}
203217

@@ -235,7 +249,7 @@ public function delete($key)
235249

236250
try {
237251
$this->service->objects->delete($this->bucket, $path);
238-
} catch (\Google_Service_Exception $e) {
252+
} catch (ServiceException $e) {
239253
return false;
240254
}
241255

@@ -259,7 +273,7 @@ public function rename($sourceKey, $targetKey)
259273
try {
260274
$this->service->objects->copy($this->bucket, $sourcePath, $this->bucket, $targetPath, $object);
261275
$this->service->objects->delete($this->bucket, $sourcePath);
262-
} catch (\Google_Service_Exception $e) {
276+
} catch (ServiceException $e) {
263277
return false;
264278
}
265279

@@ -301,7 +315,7 @@ public function listKeys($prefix = '')
301315
$reflectionProperty->setAccessible(true);
302316
$reflectionProperty->setValue($list, 'items');
303317

304-
/** @var \Google_Service_Storage_StorageObject $object */
318+
/** @var StorageObject $object */
305319
foreach ($list as $object) {
306320
$keys[] = $object->name;
307321
}
@@ -347,7 +361,39 @@ protected function ensureBucketExists()
347361
$this->bucketExists = true;
348362

349363
return;
350-
} catch (\Google_Service_Exception $e) {
364+
} catch (ServiceException $e) {
365+
if ($this->options[self::OPTION_CREATE_BUCKET_IF_NOT_EXISTS]) {
366+
if (!isset($this->options[self::OPTION_PROJECT_ID])) {
367+
throw new \RuntimeException(
368+
sprintf('Option "%s" missing, cannot create bucket', self::OPTION_PROJECT_ID)
369+
);
370+
}
371+
if (!isset($this->options[self::OPTION_LOCATION])) {
372+
throw new \RuntimeException(
373+
sprintf('Option "%s" missing, cannot create bucket', self::OPTION_LOCATION)
374+
);
375+
}
376+
377+
$bucketIamConfigDetail = new BucketIamConfigurationUniformBucketLevelAccess();
378+
$bucketIamConfigDetail->setEnabled(true);
379+
$bucketIam = new BucketIamConfiguration();
380+
$bucketIam->setUniformBucketLevelAccess($bucketIamConfigDetail);
381+
$bucket = new Bucket();
382+
$bucket->setName($this->bucket);
383+
$bucket->setLocation($this->options[self::OPTION_LOCATION]);
384+
$bucket->setStorageClass($this->options[self::OPTION_STORAGE_CLASS]);
385+
$bucket->setIamConfiguration($bucketIam);
386+
387+
$this->service->buckets->insert(
388+
$this->options[self::OPTION_PROJECT_ID],
389+
$bucket
390+
);
391+
392+
$this->bucketExists = true;
393+
394+
return;
395+
}
396+
351397
$this->bucketExists = false;
352398

353399
throw new \RuntimeException(
@@ -372,13 +418,13 @@ protected function computePath($key)
372418
* @param string $path
373419
* @param array $options
374420
*
375-
* @return bool|\Google_Service_Storage_StorageObject
421+
* @return bool|StorageObject
376422
*/
377423
private function getObjectData($path, $options = [])
378424
{
379425
try {
380426
return $this->service->objects->get($this->bucket, $path, $options);
381-
} catch (\Google_Service_Exception $e) {
427+
} catch (ServiceException $e) {
382428
return false;
383429
}
384430
}

‎tests/Gaufrette/Functional/Adapter/GoogleCloudStorageTest.php

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Gaufrette\Functional\Adapter;
44

5+
use Gaufrette\Adapter\GoogleCloudStorage;
6+
57
/**
68
* Functional tests for the GoogleCloudStorage adapter.
79
*
@@ -15,18 +17,16 @@ class GoogleCloudStorageTest extends FunctionalTestCase
1517
/**
1618
* @test
1719
* @group functional
18-
*
19-
* @expectedException \RuntimeException
2020
*/
2121
public function shouldThrowExceptionIfBucketMissing()
2222
{
23+
$this->expectException(\RuntimeException::class);
2324
/** @var \Gaufrette\Adapter\GoogleCloudStorage $adapter */
2425
$adapter = $this->filesystem->getAdapter();
25-
$oldBucket = $adapter->getOptions();
26+
$adapter->setOptions([GoogleCloudStorage::OPTION_CREATE_BUCKET_IF_NOT_EXISTS => false]);
2627
$adapter->setBucket('Gaufrette-' . mt_rand());
2728

2829
$adapter->read('foo');
29-
$adapter->setBucket($oldBucket);
3030
}
3131

3232
/**
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
11
<?php
22

3-
$client_id = 'xxxxxxxxxxxxxxx.apps.googleusercontent.com';
4-
$service_account_name = '[email protected]';
5-
$key_file_location = 'key.p12';
6-
$bucket_name = 'mybucket';
3+
use Gaufrette\Adapter\GoogleCloudStorage;
74

8-
$client = new \Google_Client();
5+
$keyFileLocation = '/home/me/path/to/service-auth-key.json';
6+
$bucketName = 'gaufrette-bucket-test-' . uniqid();
7+
$projectId = 'your-project-id-000';
8+
$bucketLocation = 'EUROPE-WEST9';
9+
10+
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $keyFileLocation);
11+
12+
$client = new \Google\Client();
913
$client->setApplicationName('Gaufrette');
14+
$client->addScope(Google\Service\Storage::DEVSTORAGE_FULL_CONTROL);
15+
$client->useApplicationDefaultCredentials();
1016

11-
$key = file_get_contents($key_file_location);
12-
$cred = new \Google_Auth_AssertionCredentials(
13-
$service_account_name,
14-
array(\Google_Service_Storage::DEVSTORAGE_FULL_CONTROL),
15-
$key
16-
);
17-
$client->setAssertionCredentials($cred);
18-
if ($client->getAuth()->isAccessTokenExpired()) {
19-
$client->getAuth()->refreshTokenWithAssertion($cred);
20-
}
17+
$service = new \Google\Service\Storage($client);
2118

22-
$service = new \Google_Service_Storage($client);
23-
return new Gaufrette\Adapter\GoogleCloudStorage($service, $bucket_name, array(), true);
19+
return new GoogleCloudStorage(
20+
$service,
21+
$bucketName,
22+
[
23+
GoogleCloudStorage::OPTION_CREATE_BUCKET_IF_NOT_EXISTS => true,
24+
GoogleCloudStorage::OPTION_PROJECT_ID => $projectId,
25+
GoogleCloudStorage::OPTION_LOCATION => $bucketLocation,
26+
],
27+
true
28+
);

0 commit comments

Comments
 (0)