Skip to content

Commit 985df46

Browse files
committed
feat(authentication): Add support for app authentication.
The current changes allows user to choose two modes of authentication when using the demo. App authentication and user authentication. To use app authentication, the user will need to select a newly added checkbox.
1 parent 0394995 commit 985df46

14 files changed

+488
-231
lines changed
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
/**
17+
* Creates a space in Google Chat with the provided title and members, and posts an
18+
* initial message to it.
19+
*
20+
* @param {Object} formData the data submitted by the user. It should contain the fields
21+
* title, description, and users.
22+
* @return {string} the resource name of the new space.
23+
*/
24+
function handleIncident(formData) {
25+
console.log(formData)
26+
const appCredentialsMode = formData.appCredentials; // Get the appCredentials element
27+
if(appCredentialsMode){
28+
return handleIncidentWithAppCredentials(formData)
29+
} else{
30+
return handleIncidentWithHumanCredentials(formData)
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/**
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
function handleIncidentWithAppCredentials(formData) {
18+
const users = formData.users.trim().length > 0 ? formData.users.split(',') : [];
19+
const spaceName = createChatSpaceWithAppCredentials(formData.title);
20+
createHumanMembershipWithAppCredentials(spaceName, getUserEmail());
21+
for (const user of users ){
22+
createHumanMembershipWithAppCredentials(spaceName, user);
23+
}
24+
createMessageWithAppCredentials(spaceName, formData.description);
25+
return spaceName;
26+
}
27+
28+
29+
30+
function createChatSpaceWithAppCredentials(spaceName) {
31+
try {
32+
const service = getService_();
33+
if (!service.hasAccess()) {
34+
console.error(service.getLastError());
35+
return;
36+
}
37+
// for private apps, the alias can be used
38+
const my_customer_alias = "customers/my_customer"
39+
// Specify the space to create.
40+
const space = {
41+
displayName: spaceName,
42+
spaceType: 'SPACE',
43+
customer: my_customer_alias
44+
};
45+
// Call Chat API with a service account to create a message.
46+
const createdSpace = Chat.Spaces.create(
47+
space,
48+
{},
49+
// Authenticate with the service account token.
50+
{'Authorization': 'Bearer ' + service.getAccessToken()});
51+
// Log details about the created message.
52+
console.log(createdSpace);
53+
return createdSpace.name;
54+
} catch (err) {
55+
// TODO (developer) - Handle exception.
56+
console.log('Failed to create space with error %s', err.message);
57+
}
58+
}
59+
60+
function createMessageWithAppCredentials(spaceName, message) {
61+
try {
62+
const service = getService_();
63+
if (!service.hasAccess()) {
64+
console.error(service.getLastError());
65+
return;
66+
}
67+
68+
// Call Chat API with a service account to create a message.
69+
const result = Chat.Spaces.Messages.create(
70+
{'text': message},
71+
spaceName,
72+
{},
73+
// Authenticate with the service account token.
74+
{'Authorization': 'Bearer ' + service.getAccessToken()});
75+
76+
// Log details about the created message.
77+
console.log(result);
78+
} catch (err) {
79+
// TODO (developer) - Handle exception.
80+
console.log('Failed to create message with error %s', err.message);
81+
}
82+
}
83+
84+
function createHumanMembershipWithAppCredentials(spaceName, email){
85+
try{
86+
const service = getService_();
87+
if (!service.hasAccess()) {
88+
console.error(service.getLastError());
89+
return;
90+
}
91+
const membership = {
92+
member: {
93+
// TODO(developer): Replace USER_NAME here
94+
name: 'users/'+email,
95+
// User type for the membership
96+
type: 'HUMAN'
97+
}
98+
};
99+
const result = Chat.Spaces.Members.create(
100+
membership,
101+
spaceName,
102+
{},
103+
{'Authorization': 'Bearer ' + service.getAccessToken()}
104+
);
105+
console.log(result)
106+
} catch (err){
107+
console.log('Failed to create membership with error %s', err.message)
108+
}
109+
110+
}
111+
112+
113+
function getService_() {
114+
return OAuth2.createService(APP_CREDENTIALS.client_email)
115+
.setTokenUrl('https://oauth2.googleapis.com/token')
116+
.setPrivateKey(APP_CREDENTIALS.private_key)
117+
.setIssuer(APP_CREDENTIALS.client_email)
118+
.setSubject(APP_CREDENTIALS.client_email)
119+
.setScope(APP_CREDENTIALS_SCOPES)
120+
.setPropertyStore(PropertiesService.getScriptProperties());
121+
}

apps-script/incident-response/ChatSpaceCreator.gs apps-script/incident-response/ChatApiHumanCredentials.gs

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2023 Google LLC
2+
* Copyright 2025 Google LLC
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,11 +23,11 @@
2323
* title, description, and users.
2424
* @return {string} the resource name of the new space.
2525
*/
26-
function createChatSpace(formData) {
26+
function handleIncidentWithHumanCredentials(formData) {
2727
const users = formData.users.trim().length > 0 ? formData.users.split(',') : [];
28-
const spaceName = setUpSpace_(formData.title, users);
29-
addAppToSpace_(spaceName);
30-
createMessage_(spaceName, formData.description);
28+
const spaceName = setUpSpaceWithHumanCredentials(formData.title, users);
29+
addAppToSpaceWithHumanCredentials(spaceName);
30+
createMessageWithHumanCredentials(spaceName, formData.description);
3131
return spaceName;
3232
}
3333

@@ -36,7 +36,7 @@ function createChatSpace(formData) {
3636
*
3737
* @return {string} the resource name of the new space.
3838
*/
39-
function setUpSpace_(displayName, users) {
39+
function setUpSpaceWithHumanCredentials(displayName, users) {
4040
const memberships = users.map(email => ({
4141
member: {
4242
name: `users/${email}`,
@@ -61,7 +61,7 @@ function setUpSpace_(displayName, users) {
6161
*
6262
* @return {string} the resource name of the new membership.
6363
*/
64-
function addAppToSpace_(spaceName) {
64+
function addAppToSpaceWithHumanCredentials(spaceName) {
6565
const request = {
6666
member: {
6767
name: "users/app",
@@ -78,7 +78,7 @@ function addAppToSpace_(spaceName) {
7878
*
7979
* @return {string} the resource name of the new message.
8080
*/
81-
function createMessage_(spaceName, text) {
81+
function createMessageWithHumanCredentials(spaceName, text) {
8282
const request = {
8383
text: text
8484
};
@@ -87,4 +87,4 @@ function createMessage_(spaceName, text) {
8787
return message.name;
8888
}
8989

90-
// [END chat_incident_response_space_creator]
90+
// [END chat_incident_response_space_creator]

apps-script/incident-response/ChatApp.gs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2023 Google LLC
2+
* Copyright 2025 Google LLC
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

apps-script/incident-response/Consts.gs

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2023 Google LLC
2+
* Copyright 2025 Google LLC
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,8 +15,11 @@
1515
*/
1616
// [START chat_incident_response_consts]
1717

18-
const PROJECT_ID = 'replace-with-your-project-id';
19-
const VERTEX_AI_LOCATION_ID = 'us-central1';
20-
const CLOSE_INCIDENT_COMMAND_ID = 1;
18+
const PROJECT_ID = PROJECT_ID';
19+
const CLOSE_INCIDENT_COMMAND_ID = 3;
20+
const APP_CREDENTIALS = APP_CREDENTIALS;
21+
const APP_CREDENTIALS_SCOPES = 'https://www.googleapis.com/auth/chat.bot https://www.googleapis.com/auth/chat.app.memberships https://www.googleapis.com/auth/chat.app.spaces.create';
22+
const GEMINI_API_KEY = GEMINI_API_KEY;
2123

22-
// [END chat_incident_response_consts]
24+
25+
// [END chat_incident_response_consts]

apps-script/incident-response/DocsApi.gs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2023 Google LLC
2+
* Copyright 2025 Google LLC
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
// [START chat_incident_response_gemini]
17+
18+
/**
19+
* Summarizes a Chat conversation using the Vertex AI text prediction API.
20+
*
21+
* @param {string} chatHistory The Chat history that will be summarized.
22+
* @return {string} The content from the text prediction response.
23+
*/
24+
function summarizeChatHistory_(chatHistory) {
25+
const prompt =
26+
"Summarize the following conversation between Engineers resolving an incident"
27+
+ " in a few sentences. Use only the information from the conversation.\n\n"
28+
+ chatHistory;
29+
30+
const payload = {
31+
"contents": [{
32+
"parts": [{
33+
"text": prompt
34+
}]
35+
}]
36+
};
37+
38+
const fetchOptions = {
39+
method: 'POST',
40+
contentType: 'application/json',
41+
payload: JSON.stringify(payload)
42+
}
43+
const gemini_endpoint = `https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=${GEMINI_API_KEY}`;
44+
45+
try {
46+
var response = UrlFetchApp.fetch(gemini_endpoint, fetchOptions);
47+
var responseCode = response.getResponseCode();
48+
var responseBody = response.getContentText();
49+
50+
if (responseCode === 200) {
51+
var json = JSON.parse(responseBody);
52+
if (json.candidates && json.candidates.length > 0 && json.candidates[0].content && json.candidates[0].content.parts && json.candidates[0].content.parts.length > 0) {
53+
return json.candidates[0].content.parts[0].text;
54+
} else {
55+
return "Gemini API: Unexpected response structure.";
56+
}
57+
} else {
58+
return "Gemini API Error: " + responseCode + " - " + responseBody;
59+
}
60+
} catch (e) {
61+
console.log("Error during API call: " + e.toString())
62+
return 'Gemini API error, please check logs.'
63+
}
64+
}
65+
66+
// [END chat_incident_response_gemini]

apps-script/incident-response/Index.html

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
Copyright 2023 Google LLC
2+
Copyright 2025 Google LLC
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -45,6 +45,10 @@ <h1>Incident Manager</h1>
4545
<small>This message will be posted after the space is created.</small><br/>
4646
<textarea name="description" id="description"></textarea>
4747
</p>
48+
<p>
49+
<label for="appCredentials">Use app credentials</label>
50+
<input type="checkbox" id="appCredentials" name="appCredentials" value="true">
51+
</p>
4852
<p class="text-center">
4953
<input type="submit" value="CREATE CHAT SPACE" />
5054
</p>
@@ -59,4 +63,4 @@ <h1>Incident Manager</h1>
5963
<?!= include('JavaScript'); ?>
6064
</body>
6165
</html>
62-
<!-- [END chat_incident_response_index] -->
66+
<!-- [END chat_incident_response_index] -->

0 commit comments

Comments
 (0)