11import {
22 onInAppFeedbackPublished } from "firebase-functions/v2/alerts/appDistribution" ;
3+ import { defineInt , defineSecret , defineString } from "firebase-functions/params" ;
4+ import logger from "firebase-functions/logger" ;
35import fetch from "node-fetch" ;
46import { FormData } from "formdata-polyfill/esm.min.js" ;
5- import logger from "firebase-functions/logger" ;
67
7- const JIRA_URI = "https://mysite.atlassian.net" ;
8- const PROJECT_KEY = "XY" ;
9- const ISSUE_TYPE_ID = "10001" ;
10- const ISSUE_LABELS = [ "in-app" ] ;
11- const API_KEY_OWNER = "user@e,mail" ;
12- const API_KEY = "am9zaHVhIHBhc3N3b3JkMTIz" ;
13- const AUTH_HEADER = "Basic " +
14- Buffer . from ( `${ API_KEY_OWNER } :${ API_KEY } ` ) . toString ( "base64" ) ;
8+ // The keys are either defined in .env or they are created
9+ // via prompt in the CLI before deploying
10+ const jiraUriConfig = defineString ( "JIRA_URI" , {
11+ description : "URI of your Jira instance (e.g. 'https://mysite.atlassian.net')" ,
12+ input : {
13+ text : {
14+ validationRegex : / ^ h t t p s : \/ \/ .* / ,
15+ validationErrorMessage : "Please enter an 'https://' URI" ,
16+ } ,
17+ } ,
18+ } ) ;
19+ const projectKeyConfig = defineString ( "PROJECT_KEY" , {
20+ description : "Project key of your Jira instance (e.g. 'XY')" ,
21+ } ) ;
22+ const issueTypeIdConfig = defineInt ( "ISSUE_TYPE_ID" , {
23+ description : "Issue type ID for the Jira issues being created" ,
24+ default : 10001 ,
25+ } ) ;
26+ const issueLabelConfig = defineString ( "ISSUE_LABEL" , {
27+ description : "Label for the Jira issues being created" ,
28+ default : "in-app" ,
29+ } ) ;
30+ const apiKeyOwnerConfig = defineString ( "API_KEY_OWNER" , {
31+ description : "Owner of the Jira API key" ,
32+ input : {
33+ text : {
34+ validationRegex :
35+ / ^ [ a - z A - Z 0 - 9 . ! # $ % & ’ * + / = ? ^ _ ` { | } ~ - ] + @ [ a - z A - Z 0 - 9 - ] + (?: \. [ a - z A - Z 0 - 9 - ] + ) * $ / ,
36+ validationErrorMessage : "Please enter a valid email address" ,
37+ } ,
38+ } ,
39+ } ) ;
40+ const apiKeyConfig = defineSecret ( "API_KEY" , {
41+ description : "Jira API key" ,
42+ } ) ;
1543
1644export const handleInAppFeedback = async ( event ) => {
1745 const issueUri = await createIssue ( event ) ;
@@ -21,7 +49,18 @@ export const handleInAppFeedback = async (event) => {
2149 return true ;
2250} ;
2351
24- export const feedbacktojira = onInAppFeedbackPublished ( handleInAppFeedback ) ;
52+ export const feedbacktojira =
53+ onInAppFeedbackPublished ( { secrets : [ apiKeyConfig ] } , handleInAppFeedback ) ;
54+
55+ /**
56+ * Creates "Authorization" header value.
57+ * @return {string } Basic, base64-encoded "Authorization" header value
58+ */
59+ function authHeader ( ) {
60+ return "Basic " + Buffer
61+ . from ( apiKeyOwnerConfig . value ( ) + ":" + apiKeyConfig . value ( ) )
62+ . toString ( "base64" ) ;
63+ }
2564
2665/**
2766 * Creates new issue in Jira.
@@ -31,10 +70,10 @@ async function createIssue(event) {
3170 const requestJson = await buildCreateIssueRequest ( event ) ;
3271 const requestBody = JSON . stringify ( requestJson ) ;
3372 const response =
34- await fetch ( "https://eventarc.atlassian.net /rest/api/3/issue" , {
73+ await fetch ( ` ${ jiraUriConfig . value ( ) } /rest/api/3/issue` , {
3574 method : "POST" ,
3675 headers : {
37- "Authorization" : AUTH_HEADER ,
76+ "Authorization" : authHeader ( ) ,
3877 "Accept" : "application/json" ,
3978 "Content-Type" : "application/json" ,
4079 } ,
@@ -68,7 +107,7 @@ async function uploadScreenshot(issueUri, screenshotUri) {
68107 method : "POST" ,
69108 body : form ,
70109 headers : {
71- "Authorization" : AUTH_HEADER ,
110+ "Authorization" : authHeader ( ) ,
72111 "Accept" : "application/json" ,
73112 "X-Atlassian-Token" : "no-check" ,
74113 } ,
@@ -85,10 +124,14 @@ async function uploadScreenshot(issueUri, screenshotUri) {
85124 */
86125async function lookupReporter ( testerEmail ) {
87126 const response =
88- await fetch ( `${ JIRA_URI } /rest/api/3/user/search?query=${ testerEmail } ` , {
89- method : "GET" ,
90- headers : { "Authorization" : AUTH_HEADER , "Accept" : "application/json" } ,
91- } ) ;
127+ await fetch (
128+ `${ jiraUriConfig . value ( ) } /rest/api/3/user/search` +
129+ `?query=${ testerEmail } ` , {
130+ method : "GET" ,
131+ headers : {
132+ "Authorization" : authHeader ( ) ,
133+ "Accept" : "application/json" ,
134+ } } ) ;
92135 if ( ! response . ok ) {
93136 logger . info ( `Failed to find Jira user for '${ testerEmail } ':` +
94137 `${ response . status } ${ response . statusText } ` ) ;
@@ -112,10 +155,10 @@ async function buildCreateIssueRequest(event) {
112155 "fields" : {
113156 "summary" : summary ,
114157 "issuetype" : {
115- "id" : ISSUE_TYPE_ID ,
158+ "id" : issueTypeIdConfig . value ( ) ,
116159 } ,
117160 "project" : {
118- "key" : PROJECT_KEY ,
161+ "key" : projectKeyConfig . value ( ) ,
119162 } ,
120163 "description" : {
121164 "type" : "doc" ,
@@ -231,7 +274,7 @@ async function buildCreateIssueRequest(event) {
231274 } ,
232275 ] ,
233276 } ,
234- "labels" : ISSUE_LABELS ,
277+ "labels" : [ issueLabelConfig . value ( ) ] ,
235278 } ,
236279 } ;
237280 const reporter = await lookupReporter ( event . data . payload . testerEmail ) ;
0 commit comments