Skip to content

Commit a1d7c4c

Browse files
Ace Nassribshafferpattishin
authored
feat(functions): add Firebase auth sample (GoogleCloudPlatform#1337)
* feat(functions): add Firebase auth sample * Fix lint Co-authored-by: Brent Shaffer Co-authored-by: Patti Shin
1 parent 3e20545 commit a1d7c4c

File tree

5 files changed

+320
-0
lines changed

5 files changed

+320
-0
lines changed

functions/firebase_auth/composer.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"require": {
3+
"google/cloud-functions-framework": "^0.7.1"
4+
},
5+
"scripts": {
6+
"start": [
7+
"Composer\\Config::disableProcessTimeout",
8+
"FUNCTION_SIGNATURE_TYPE=cloudevent FUNCTION_TARGET=firebaseAuth php -S localhost:${PORT:-8080} vendor/bin/router.php"
9+
]
10+
},
11+
"require-dev": {
12+
"google/auth": "^1.14",
13+
"google/cloud-logging": "^1.21"
14+
}
15+
}

functions/firebase_auth/index.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
2+
/**
3+
* Copyright 2021 Google LLC.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
// [START functions_firebase_auth]
19+
20+
use Google\CloudFunctions\CloudEvent;
21+
22+
function firebaseAuth(CloudEvent $cloudevent)
23+
{
24+
$log = fopen(getenv('LOGGER_OUTPUT') ?: 'php://stderr', 'wb');
25+
$data = $cloudevent->getData();
26+
27+
fwrite(
28+
$log,
29+
'Function triggered by change to user: ' . $data['uid'] . PHP_EOL
30+
);
31+
fwrite($log, 'Created at: ' . $data['metadata']['createTime'] . PHP_EOL);
32+
33+
if (isset($data['email'])) {
34+
fwrite($log, 'Email: ' . $data['email'] . PHP_EOL);
35+
}
36+
}
37+
// [END functions_firebase_auth]
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
xml version="1.0" encoding="UTF-8"?>
2+
17+
<phpunit bootstrap="../../testing/bootstrap.php" convertWarningsToExceptions="false">
18+
<testsuites>
19+
<testsuite name="Cloud Functions Firebase Auth Test Suite">
20+
<directory>testdirectory>
21+
testsuite>
22+
testsuites>
23+
<logging>
24+
<log type="coverage-clover" target="build/logs/clover.xml"/>
25+
logging>
26+
<filter>
27+
<whitelist>
28+
<directory suffix=".php">.directory>
29+
<exclude>
30+
<directory>./vendordirectory>
31+
exclude>
32+
whitelist>
33+
filter>
34+
phpunit>
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
2+
/**
3+
* Copyright 2021 Google LLC.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace Google\Cloud\Samples\Functions\FirebaseAuth\Test;
21+
22+
use Google\Auth\ApplicationDefaultCredentials;
23+
use Google\Auth\CredentialsLoader;
24+
use Google\Cloud\Logging\LoggingClient;
25+
use Google\Cloud\TestUtils\CloudFunctionDeploymentTrait;
26+
use Google\Cloud\TestUtils\EventuallyConsistentTestTrait;
27+
use PHPUnit\Framework\TestCase;
28+
29+
/**
30+
* Class DeployTest.
31+
* @group deploy
32+
*
33+
* This test is not run by the CI system.
34+
*
35+
* To skip deployment of a new function, run with "GOOGLE_SKIP_DEPLOYMENT=true".
36+
* To skip deletion of the tested function, run with "GOOGLE_KEEP_DEPLOYMENT=true".
37+
*/
38+
class DeployTest extends TestCase
39+
{
40+
use CloudFunctionDeploymentTrait;
41+
use EventuallyConsistentTestTrait;
42+
43+
/** @var string */
44+
private static $entryPoint = 'firebaseAuth';
45+
46+
/** @var string */
47+
private static $functionSignatureType = 'cloudevent';
48+
49+
/** @var LoggingClient */
50+
private static $loggingClient;
51+
52+
/** @var \GuzzleHttp\Client */
53+
private static $apiHttpClient;
54+
55+
/**
56+
* Deploy the Cloud Function, called from DeploymentTrait::deployApp().
57+
*
58+
* Overrides CloudFunctionDeploymentTrait::doDeploy().
59+
*/
60+
private static function doDeploy()
61+
{
62+
$event = 'providers/firebase.auth/eventTypes/user.create';
63+
64+
return self::$fn->deploy([
65+
'--trigger-event' => $event
66+
], '');
67+
}
68+
69+
public function dataProvider()
70+
{
71+
$email = uniqid();
72+
return [
73+
[
74+
'label' => 'Listens to Auth events',
75+
'email' => $email . '@example.com',
76+
'expected' => $email . '@example.com'
77+
]
78+
];
79+
}
80+
81+
/**
82+
* @dataProvider dataProvider
83+
*/
84+
public function testFirebaseAuth(
85+
string $label,
86+
string $email,
87+
string $expected
88+
): void {
89+
// Trigger user creation.
90+
$this->createAuthUser($email);
91+
92+
// Give event and log systems a head start.
93+
// If log retrieval fails to find logs for our function within retry limit, increase sleep time.
94+
sleep(10);
95+
96+
$fiveMinAgo = date(\DateTime::RFC3339, strtotime('-5 minutes'));
97+
$this->processFunctionLogs($fiveMinAgo, function (\Iterator $logs) use ($expected, $label) {
98+
// Concatenate all relevant log messages.
99+
$actual = '';
100+
foreach ($logs as $log) {
101+
$info = $log->info();
102+
if (isset($info['textPayload'])) {
103+
$actual .= $info['textPayload'];
104+
}
105+
}
106+
107+
// Only testing one property to decrease odds the expected logs are
108+
// split between log requests.
109+
$this->assertStringContainsString($expected, $actual, $label);
110+
});
111+
}
112+
113+
/**
114+
* Create a new Firebase Auth user.
115+
*
116+
* @param string $email The key to update.
117+
* @param string $value The value to set the key to.
118+
*
119+
* @throws \RuntimeException
120+
*/
121+
private function createAuthUser(string $email): void
122+
{
123+
if (empty(self::$apiHttpClient)) {
124+
$credentials = ApplicationDefaultCredentials::getCredentials('https://www.googleapis.com/auth/cloud-platform');
125+
self::$apiHttpClient = CredentialsLoader::makeHttpClient($credentials, [
126+
'base_uri' => 'https://identitytoolkit.googleapis.com/'
127+
]);
128+
}
129+
130+
// Create the account
131+
$createResponse = (string) self::$apiHttpClient->post('/v1/accounts:signUp', [
132+
'headers' => ['If-Match' => '*'],
133+
'json' => [
134+
'email' => $email,
135+
'password' => uniqid(),
136+
'returnSecureToken' => true
137+
]
138+
])->getBody();
139+
140+
$idToken = json_decode($createResponse, true)['localId'];
141+
142+
// Delete the account (to clean up after the test)
143+
self::$apiHttpClient->post('/v1/accounts:delete', [
144+
'headers' => ['If-Match' => '*'],
145+
'json' => [
146+
'localId' => $idToken
147+
]
148+
]);
149+
}
150+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
2+
/**
3+
* Copyright 2021 Google LLC.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace Google\Cloud\Samples\Functions\FirebaseAuth\Test;
21+
22+
use PHPUnit\Framework\TestCase;
23+
use Google\CloudFunctions\CloudEvent;
24+
use Google\Cloud\TestUtils\CloudFunctionLocalTestTrait;
25+
26+
/**
27+
* Class IntegrationTest.
28+
*
29+
* Integration Test for firebaseAuth.
30+
*/
31+
class IntegrationTest extends TestCase
32+
{
33+
use CloudFunctionLocalTestTrait;
34+
35+
/** @var string */
36+
private static $entryPoint = 'firebaseAuth';
37+
38+
/** @var string */
39+
private static $functionSignatureType = 'cloudevent';
40+
41+
public function dataProvider()
42+
{
43+
return [
44+
[
45+
'cloudevent' => CloudEvent::fromArray([
46+
'id' => uniqid(),
47+
'source' => 'firebase.googleapis.com',
48+
'specversion' => '1.0',
49+
'type' => 'google.firebase.auth.user.v1.created',
50+
'data' => [
51+
'uid' => 'me',
52+
'email' => '[email protected]',
53+
'metadata' => ['createdAt' => date('c')],
54+
],
55+
]),
56+
'statusCode' => '200',
57+
],
58+
];
59+
}
60+
61+
/**
62+
* @dataProvider dataProvider
63+
*/
64+
public function testFirebaseRemoteConfig(
65+
CloudEvent $cloudevent,
66+
string $statusCode
67+
): void {
68+
// Send an HTTP request using CloudEvent.
69+
$resp = $this->request($cloudevent);
70+
71+
// The Cloud Function logs all data to stderr.
72+
$actual = self::$localhost->getIncrementalErrorOutput();
73+
74+
// Confirm the status code.
75+
$this->assertEquals($statusCode, $resp->getStatusCode());
76+
77+
// Verify the data properties are logged by the function.
78+
foreach ($cloudevent->getData() as $property => $value) {
79+
if (is_string($value)) {
80+
$this->assertStringContainsString($value, $actual);
81+
}
82+
}
83+
}
84+
}

0 commit comments

Comments
 (0)