Skip to content

Commit 1aa140d

Browse files
authored
Merge branch 'main' into secretmanager-delayed-destroy-samples
2 parents 8f5f4d3 + 54369af commit 1aa140d

33 files changed

+306
-35
lines changed

.kokoro/secrets-example.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ export GOOGLE_KMS_SERVICEACCOUNTEMAIL=
100100
export REDIS_HOST=
101101
export REDIS_PORT=
102102

103+
# Model Armor
104+
export MA_FOLDER_ID=
105+
export MA_ORG_ID=
106+
103107
# PubSub
104108
export GOOGLE_PUBSUB_SUBSCRIPTION=php-example-subscription
105109
export GOOGLE_PUBSUB_TOPIC=php-example-topic

.kokoro/secrets.sh.enc

78 Bytes
Binary file not shown.

modelarmor/composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"require": {
3-
"google/cloud-dlp": "^2.4",
4-
"google/cloud-modelarmor": "^0.1.0"
3+
"google/cloud-dlp": "^2.6",
4+
"google/cloud-modelarmor": "^0.4.0"
55
}
66
}

modelarmor/test/modelarmorTest.php

Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
use Google\Cloud\ModelArmor\V1\RaiFilterType;
5151
use Google\Cloud\ModelArmor\V1\RaiFilterSettings;
5252
use Google\Cloud\ModelArmor\V1\RaiFilterSettings\RaiFilter;
53+
use Google\Cloud\ModelArmor\V1\FloorSetting;
54+
use Google\Cloud\ModelArmor\V1\UpdateFloorSettingRequest;
5355

5456
class modelarmorTest extends TestCase
5557
{
@@ -76,6 +78,8 @@ class modelarmorTest extends TestCase
7678
protected static $testRaiTemplateId;
7779
protected static $testMaliciousTemplateId;
7880
protected static $testPIandJailbreakTemplateId;
81+
protected static $organizationId;
82+
protected static $folderId;
7983

8084
public static function setUpBeforeClass(): void
8185
{
@@ -96,10 +100,23 @@ public static function setUpBeforeClass(): void
96100
self::$testSanitizeModelResponseUserPromptId = self::getTemplateId('php-sanitize-model-response-user-prompt-');
97101
self::$testRaiTemplateId = self::getTemplateId('php-rai-template-');
98102
self::$testMaliciousTemplateId = self::getTemplateId('php-malicious-template-');
99-
self::$testPIandJailbreakTemplateId = self::getTemplateId('php-pi-and-jailbreak-template-');
103+
self::$testPIandJailbreakTemplateId = self::getTemplateId('php-template-with-pijailbreak-');
104+
self::$organizationId = self::requireEnv('MA_ORG_ID');
105+
self::$folderId = self::requireEnv('MA_FOLDER_ID');
100106
self::createTemplateWithMaliciousURI();
101107
self::createTemplateWithPIJailbreakFilter();
102108
self::createTemplateWithRAI();
109+
110+
// Reset floor settings before tests
111+
if (self::$projectId) {
112+
self::resetFloorSettings('project', self::$projectId);
113+
}
114+
if (self::$folderId) {
115+
self::resetFloorSettings('folder', self::$folderId);
116+
}
117+
if (self::$organizationId) {
118+
self::resetFloorSettings('organization', self::$organizationId);
119+
}
103120
}
104121

105122
public static function tearDownAfterClass(): void
@@ -122,6 +139,18 @@ public static function tearDownAfterClass(): void
122139
self::deleteTemplate(self::$projectId, self::$locationId, self::$testMaliciousTemplateId);
123140
self::deleteTemplate(self::$projectId, self::$locationId, self::$testPIandJailbreakTemplateId);
124141
self::deleteDlpTemplates(self::$inspectTemplateName, self::$deidentifyTemplateName, self::$locationId);
142+
143+
// Reset floor settings after tests
144+
if (self::$projectId) {
145+
self::resetFloorSettings('project', self::$projectId);
146+
}
147+
if (self::$folderId) {
148+
self::resetFloorSettings('folder', self::$folderId);
149+
}
150+
if (self::$organizationId) {
151+
self::resetFloorSettings('organization', self::$organizationId);
152+
}
153+
125154
self::$client->close();
126155
}
127156

@@ -143,6 +172,48 @@ public static function getTemplateId(string $testId): string
143172
return uniqid($testId);
144173
}
145174

175+
/**
176+
* Resets floor settings to default values for various resource types
177+
*
178+
* @param string $resourceType The type of resource (project, folder, organization)
179+
* @param string $resourceId The ID of the resource
180+
*/
181+
protected static function resetFloorSettings(string $resourceType, string $resourceId): void
182+
{
183+
try {
184+
$client = new ModelArmorClient();
185+
186+
// Format resource path based on resource type
187+
$resourcePathFormat = match($resourceType) {
188+
'project' => 'projects/%s/locations/global/floorSetting',
189+
'folder' => 'folders/%s/locations/global/floorSetting',
190+
'organization' => 'organizations/%s/locations/global/floorSetting',
191+
default => throw new \InvalidArgumentException("Invalid resource type: {$resourceType}"),
192+
};
193+
194+
$floorSettingsName = sprintf($resourcePathFormat, $resourceId);
195+
196+
// Create an empty filter config
197+
$filterConfig = new FilterConfig();
198+
199+
// Create floor setting with enforcement disabled
200+
$floorSetting = (new FloorSetting())
201+
->setName($floorSettingsName)
202+
->setFilterConfig($filterConfig)
203+
->setEnableFloorSettingEnforcement(false);
204+
205+
$updateRequest = (new UpdateFloorSettingRequest())->setFloorSetting($floorSetting);
206+
$response = $client->updateFloorSetting($updateRequest);
207+
208+
echo "Floor settings reset for {$resourceType} {$resourceId}\n";
209+
} catch (\Exception $e) {
210+
// Log but don't fail teardown if reset fails
211+
echo "Warning: Failed to reset {$resourceType} floor settings: " . $e->getMessage() . "\n";
212+
}
213+
}
214+
215+
// Wrapper methods removed in favor of directly calling resetFloorSettings
216+
146217
public function testCreateTemplate()
147218
{
148219
$output = $this->runFunctionSnippet('create_template', [
@@ -696,5 +767,63 @@ protected static function createTemplate($templateId, $template)
696767
}
697768
}
698769

699-
# TODO: Add tests for floor settings once API issues are resolved.
770+
public function testGetFolderFloorSettings()
771+
{
772+
$output = $this->runSnippet('get_folder_floor_settings', [
773+
self::$folderId,
774+
]);
775+
776+
$expectedResponseString = 'Floor settings retrieved successfully:';
777+
$this->assertStringContainsString($expectedResponseString, $output);
778+
}
779+
780+
public function testGetProjectFloorSettings()
781+
{
782+
$output = $this->runSnippet('get_project_floor_settings', [
783+
self::$projectId,
784+
]);
785+
786+
$expectedResponseString = 'Floor settings retrieved successfully:';
787+
$this->assertStringContainsString($expectedResponseString, $output);
788+
}
789+
790+
public function testGetOrganizationFloorSettings()
791+
{
792+
$output = $this->runSnippet('get_organization_floor_settings', [
793+
self::$organizationId,
794+
]);
795+
796+
$expectedResponseString = 'Floor settings retrieved successfully:';
797+
$this->assertStringContainsString($expectedResponseString, $output);
798+
}
799+
800+
public function testUpdateFolderFloorSettings()
801+
{
802+
$output = $this->runSnippet('update_folder_floor_settings', [
803+
self::$folderId,
804+
]);
805+
806+
$expectedResponseString = 'Floor setting updated';
807+
$this->assertStringContainsString($expectedResponseString, $output);
808+
}
809+
810+
public function testUpdateProjectFloorSettings()
811+
{
812+
$output = $this->runSnippet('update_project_floor_settings', [
813+
self::$projectId,
814+
]);
815+
816+
$expectedResponseString = 'Floor setting updated';
817+
$this->assertStringContainsString($expectedResponseString, $output);
818+
}
819+
820+
public function testUpdateOrganizationFloorSettings()
821+
{
822+
$output = $this->runSnippet('update_organization_floor_settings', [
823+
self::$organizationId,
824+
]);
825+
826+
$expectedResponseString = 'Floor setting updated';
827+
$this->assertStringContainsString($expectedResponseString, $output);
828+
}
700829
}

storage/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"require": {
3-
"google/cloud-storage": "^1.28.0",
3+
"google/cloud-storage": "^1.48.7",
44
"paragonie/random_compat": "^9.0.0"
55
},
66
"require-dev": {

storage/src/configure_retries.php

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<?php
2+
3+
/**
4+
* Copyright 2025 Google Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
/**
20+
* For instructions on how to run the full sample:
21+
*
22+
* @see https://github.com/GoogleCloudPlatform/php-docs-samples/tree/main/storage/README.md
23+
*/
24+
25+
namespace Google\Cloud\Samples\Storage;
26+
27+
# [START storage_configure_retries]
28+
use Google\Cloud\Storage\StorageClient;
29+
30+
/**
31+
* Configures retries with customizations.
32+
*
33+
* @param string $bucketName The name of your Cloud Storage bucket.
34+
* (e.g. 'my-bucket')
35+
*/
36+
function configure_retries(string $bucketName): void
37+
{
38+
$storage = new StorageClient([
39+
// The maximum number of automatic retries attempted before returning
40+
// the error.
41+
// Default: 3
42+
'retries' => 10,
43+
44+
// Exponential backoff settings
45+
// Retry strategy to signify that we never want to retry an operation
46+
// even if the error is retryable.
47+
// Default: StorageClient::RETRY_IDEMPOTENT
48+
'retryStrategy' => StorageClient::RETRY_ALWAYS,
49+
50+
// Executes a delay
51+
// Defaults to utilizing `usleep`.
52+
// Function signature should match: `function (int $delay) : void`.
53+
// This function is mostly used internally, so the tests don't wait
54+
// the time of the delay to run.
55+
'restDelayFunction' => function ($delay) {
56+
usleep($delay);
57+
},
58+
59+
// Sets the conditions for determining how long to wait between attempts to retry.
60+
// Function signature should match: `function (int $attempt) : int`.
61+
// Allows to change the initial retry delay, retry delay multiplier and maximum retry delay.
62+
'restCalcDelayFunction' => fn ($attempt) => ($attempt + 1) * 100,
63+
64+
// Sets the conditions for whether or not a request should attempt to retry.
65+
// Function signature should match: `function (\Exception $ex) : bool`.
66+
'restRetryFunction' => function (\Exception $e) {
67+
// Custom logic: ex. only retry if the error code is 404.
68+
return $e->getCode() === 404;
69+
},
70+
71+
// Runs after the restRetryFunction. This might be used to simply consume the
72+
// exception and $arguments b/w retries. This returns the new $arguments thus allowing
73+
// modification on demand for $arguments. For ex: changing the headers in b/w retries.
74+
'restRetryListener' => function (\Exception $e, $retryAttempt, &$arguments) {
75+
// logic
76+
},
77+
]);
78+
$bucket = $storage->bucket($bucketName);
79+
$operationRetriesOverrides = [
80+
// The maximum number of automatic retries attempted before returning
81+
// the error.
82+
// Default: 3
83+
'retries' => 10,
84+
85+
// Exponential backoff settings
86+
// Retry strategy to signify that we never want to retry an operation
87+
// even if the error is retryable.
88+
// Default: StorageClient::RETRY_IDEMPOTENT
89+
'retryStrategy' => StorageClient::RETRY_ALWAYS,
90+
91+
// Executes a delay
92+
// Defaults to utilizing `usleep`.
93+
// Function signature should match: `function (int $delay) : void`.
94+
// This function is mostly used internally, so the tests don't wait
95+
// the time of the delay to run.
96+
'restDelayFunction' => function ($delay) {
97+
usleep($delay);
98+
},
99+
100+
// Sets the conditions for determining how long to wait between attempts to retry.
101+
// Function signature should match: `function (int $attempt) : int`.
102+
// Allows to change the initial retry delay, retry delay multiplier and maximum retry delay.
103+
'restCalcDelayFunction' => fn ($attempt) => ($attempt + 1) * 100,
104+
105+
// Sets the conditions for whether or not a request should attempt to retry.
106+
// Function signature should match: `function (\Exception $ex) : bool`.
107+
'restRetryFunction' => function (\Exception $e) {
108+
// Custom logic: ex. only retry if the error code is 404.
109+
return $e->getCode() === 404;
110+
},
111+
112+
// Runs after the restRetryFunction. This might be used to simply consume the
113+
// exception and $arguments b/w retries. This returns the new $arguments thus allowing
114+
// modification on demand for $arguments. For ex: changing the headers in b/w retries.
115+
'restRetryListener' => function (\Exception $e, $retryAttempt, &$arguments) {
116+
// logic
117+
},
118+
];
119+
foreach ($bucket->objects($operationRetriesOverrides) as $object) {
120+
printf('Object: %s' . PHP_EOL, $object->name());
121+
}
122+
}
123+
# [END storage_configure_retries]
124+
125+
// The following 2 lines are only needed to run the samples
126+
require_once __DIR__ . '/../../testing/sample_helpers.php';
127+
\Google\Cloud\Samples\execute_sample(__FILE__, __NAMESPACE__, $argv);

storage/src/move_object_atomic.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
use Google\Cloud\Storage\StorageClient;
2828

2929
/**
30-
* Move an object to a new name within HNS-enabled bucket.
30+
* Move an object to a new name within bucket.
3131
*
3232
* @param string $bucketName The name of your Cloud Storage bucket.
3333
* (e.g. 'my-bucket')

storage/test/ObjectsTest.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,16 @@ public function testManageObject()
151151
$this->assertEquals($output, $outputString);
152152
}
153153

154-
public function testMoveObjectAtomic()
154+
/**
155+
* @dataProvider provideMoveObject
156+
*/
157+
public function testMoveObjectAtomic(bool $hnEnabled)
155158
{
156-
$bucketName = self::$bucketName . '-hns';
159+
$bucketName = 'move-object-bucket-' . uniqid();
157160
$objectName = 'test-object-' . time();
158161
$newObjectName = $objectName . '-moved';
159162
$bucket = self::$storage->createBucket($bucketName, [
160-
'hierarchicalNamespace' => ['enabled' => true],
163+
'hierarchicalNamespace' => ['enabled' => $hnEnabled],
161164
'iamConfiguration' => ['uniformBucketLevelAccess' => ['enabled' => true]]
162165
]);
163166

@@ -189,6 +192,11 @@ public function testMoveObjectAtomic()
189192
$bucket->delete();
190193
}
191194

195+
public function provideMoveObject()
196+
{
197+
return [[true], [false]];
198+
}
199+
192200
public function testCompose()
193201
{
194202
$bucket = self::$storage->bucket(self::$bucketName);

vision/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "google/vision",
33
"type": "project",
44
"require": {
5-
"google/cloud-vision": "^1.0.0",
5+
"google/cloud-vision": "^1.7",
66
"google/cloud-storage": "^1.20.1"
77
}
88
}

vision/quickstart.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
require __DIR__ . '/vendor/autoload.php';
2121

2222
# imports the Google Cloud client library
23-
use Google\Cloud\Vision\V1\ImageAnnotatorClient;
23+
use Google\Cloud\Vision\V1\Client\ImageAnnotatorClient;
2424

2525
# instantiates a client
2626
$imageAnnotator = new ImageAnnotatorClient();

0 commit comments

Comments
 (0)