@@ -24,9 +24,17 @@ import { FirebaseError, toHttpResponse } from '../utils/error';
2424import { FirebaseFunctionsError , FunctionsErrorCode , FUNCTIONS_ERROR_CODE_MAPPING } from './error' ;
2525import * as utils from '../utils/index' ;
2626import * as validator from '../utils/validator' ;
27- import { TaskOptions } from './functions-api' ;
27+ import { TaskOptions , FunctionScope } from './functions-api' ;
2828import { ApplicationDefaultCredential } from '../app/credential-internal' ;
2929
30+ export type InternalFunctionScope = FunctionScope | {
31+ scope : 'kit' ;
32+ instance : string ;
33+ } | {
34+ scope : 'extensionOrKit' ;
35+ instance : string ;
36+ } ;
37+
3038const CLOUD_TASKS_API_RESOURCE_PATH = 'projects/{projectId}/locations/{locationId}/queues/{resourceId}/tasks' ;
3139const CLOUD_TASKS_API_URL_FORMAT = 'https://cloudtasks.googleapis.com/v2/' + CLOUD_TASKS_API_RESOURCE_PATH ;
3240const FIREBASE_FUNCTION_URL_FORMAT = 'https://{locationId}-{projectId}.cloudfunctions.net/{resourceId}' ;
@@ -65,10 +73,14 @@ export class FunctionsApiClient {
6573 * Deletes a task from a queue.
6674 *
6775 * @param id - The ID of the task to delete.
68- * @param functionName - The function name of the queue.
69- * @param extensionId - Optional canonical ID of the extension .
76+ * @param functionName - The name of the function associated with the queue.
77+ * @param scope - Optional `FunctionScope` configuration .
7078 */
71- public async delete ( id : string , functionName : string , extensionId ?: string ) : Promise < void > {
79+ public async delete (
80+ id : string ,
81+ functionName : string ,
82+ scope ?: InternalFunctionScope
83+ ) : Promise < void > {
7284 if ( ! validator . isNonEmptyString ( functionName ) ) {
7385 throw new FirebaseFunctionsError ( {
7486 code : 'invalid-argument' ,
@@ -101,13 +113,13 @@ export class FunctionsApiClient {
101113 message : 'No valid function name specified to enqueue tasks for.'
102114 } ) ;
103115 }
104- if ( typeof extensionId !== 'undefined' && validator . isNonEmptyString ( extensionId ) ) {
105- resources . resourceId = `ext- ${ extensionId } - ${ resources . resourceId } ` ;
106- }
116+
117+ const { resourceId } = this . resolveResourceId ( resources . resourceId , scope ) ;
118+ const targetResources = { ... resources , resourceId } ;
107119
108120 try {
109- const serviceUrl = tasksEmulatorUrl ( resources , this . emulatorHost ) ?. concat ( '/' , id )
110- ?? await this . getUrl ( resources , CLOUD_TASKS_API_URL_FORMAT . concat ( '/' , id ) ) ;
121+ const serviceUrl = tasksEmulatorUrl ( targetResources , this . emulatorHost ) ?. concat ( '/' , id )
122+ ?? await this . getUrl ( targetResources , CLOUD_TASKS_API_URL_FORMAT . concat ( '/' , id ) ) ;
111123 const request : HttpRequestConfig = {
112124 method : 'DELETE' ,
113125 url : serviceUrl ,
@@ -116,10 +128,6 @@ export class FunctionsApiClient {
116128 await this . httpClient . send ( request ) ;
117129 } catch ( err : unknown ) {
118130 if ( err instanceof RequestResponseError ) {
119- if ( err . response . status === 404 ) {
120- // if no task with the provided ID exists, then ignore the delete.
121- return ;
122- }
123131 throw this . toFirebaseError ( err ) ;
124132 } else {
125133 throw err ;
@@ -131,11 +139,16 @@ export class FunctionsApiClient {
131139 * Creates a task and adds it to a queue.
132140 *
133141 * @param data - The data payload of the task.
134- * @param functionName - The functionName of the queue.
135- * @param extensionId - Optional canonical ID of the extension .
142+ * @param functionName - The name of the function associated with the queue.
143+ * @param scope - Optional `FunctionScope` configuration .
136144 * @param opts - Optional options when enqueuing a new task.
137145 */
138- public async enqueue ( data : any , functionName : string , extensionId ?: string , opts ?: TaskOptions ) : Promise < void > {
146+ public async enqueue (
147+ data : any ,
148+ functionName : string ,
149+ scope ?: InternalFunctionScope ,
150+ opts ?: TaskOptions
151+ ) : Promise < void > {
139152 if ( ! validator . isNonEmptyString ( functionName ) ) {
140153 throw new FirebaseFunctionsError ( {
141154 code : 'invalid-argument' ,
@@ -161,17 +174,17 @@ export class FunctionsApiClient {
161174 message : 'No valid function name specified to enqueue tasks for.'
162175 } ) ;
163176 }
164- if ( typeof extensionId !== 'undefined' && validator . isNonEmptyString ( extensionId ) ) {
165- resources . resourceId = `ext-${ extensionId } -${ resources . resourceId } ` ;
166- }
167177
168- const task = this . validateTaskOptions ( data , resources , opts ) ;
178+ const { resourceId, extensionOrKitId } = this . resolveResourceId ( resources . resourceId , scope ) ;
179+ const targetResources = { ...resources , resourceId } ;
180+
169181 try {
182+ const task = this . validateTaskOptions ( data , targetResources , opts ) ;
170183 const serviceUrl =
171- tasksEmulatorUrl ( resources , this . emulatorHost ) ??
172- await this . getUrl ( resources , CLOUD_TASKS_API_URL_FORMAT ) ;
184+ tasksEmulatorUrl ( targetResources , this . emulatorHost ) ??
185+ await this . getUrl ( targetResources , CLOUD_TASKS_API_URL_FORMAT ) ;
173186
174- const taskPayload = await this . updateTaskPayload ( task , resources , extensionId ) ;
187+ const taskPayload = await this . updateTaskPayload ( task , targetResources , extensionOrKitId ) ;
175188 const request : HttpRequestConfig = {
176189 method : 'POST' ,
177190 url : serviceUrl ,
@@ -199,6 +212,51 @@ export class FunctionsApiClient {
199212 }
200213 }
201214
215+ private resolveResourceId (
216+ resourceId : string ,
217+ scope : InternalFunctionScope = { scope : 'current' }
218+ ) : { resourceId : string ; extensionOrKitId ?: string } {
219+
220+ switch ( scope . scope ) {
221+ case 'current' : {
222+ const extInstanceId = process . env . EXT_INSTANCE_ID ;
223+ if ( validator . isNonEmptyString ( extInstanceId ) ) {
224+ return {
225+ resourceId : `ext-${ extInstanceId } -${ resourceId } ` ,
226+ extensionOrKitId : extInstanceId ,
227+ } ;
228+ }
229+ const kitInstanceId = process . env . FIREBASE_KIT_INSTANCE_ID ;
230+ if ( validator . isNonEmptyString ( kitInstanceId ) ) {
231+ return {
232+ resourceId : `kit-${ kitInstanceId } -${ resourceId } ` ,
233+ extensionOrKitId : kitInstanceId ,
234+ } ;
235+ }
236+ return { resourceId } ;
237+ }
238+ case 'global' :
239+ return { resourceId } ;
240+ case 'extension' :
241+ return {
242+ resourceId : `ext-${ scope . instance } -${ resourceId } ` ,
243+ extensionOrKitId : scope . instance ,
244+ } ;
245+ case 'kit' : // kit scope is secretly accepted for forward compatibility
246+ return {
247+ resourceId : `kit-${ scope . instance } -${ resourceId } ` ,
248+ extensionOrKitId : scope . instance ,
249+ } ;
250+ case 'extensionOrKit' :
251+ return {
252+ resourceId : `ext-${ scope . instance } -${ resourceId } ` ,
253+ extensionOrKitId : scope . instance ,
254+ } ;
255+ default :
256+ return { resourceId } ;
257+ }
258+ }
259+
202260 private getUrl ( resourceName : utils . ParsedResource , urlFormat : string ) : Promise < string > {
203261 let { locationId } = resourceName ;
204262 const { projectId, resourceId } = resourceName ;
@@ -349,7 +407,11 @@ export class FunctionsApiClient {
349407 return task ;
350408 }
351409
352- private async updateTaskPayload ( task : Task , resources : utils . ParsedResource , extensionId ?: string ) : Promise < Task > {
410+ private async updateTaskPayload (
411+ task : Task ,
412+ resources : utils . ParsedResource ,
413+ extensionOrKitId ?: string
414+ ) : Promise < Task > {
353415 const defaultUrl = this . emulatorHost ?
354416 ''
355417 : await this . getUrl ( resources , FIREBASE_FUNCTION_URL_FORMAT ) ;
@@ -360,7 +422,7 @@ export class FunctionsApiClient {
360422
361423 task . httpRequest . url = functionUrl ;
362424 // When run from a deployed extension, we should be using ComputeEngineCredentials
363- if ( validator . isNonEmptyString ( extensionId ) && this . app . options . credential
425+ if ( validator . isNonEmptyString ( extensionOrKitId ) && this . app . options . credential
364426 instanceof ApplicationDefaultCredential && await this . app . options . credential . isComputeEngineCredential ( ) ) {
365427 const idToken = await this . app . options . credential . getIDToken ( functionUrl ) ;
366428 task . httpRequest . headers = { ...task . httpRequest . headers , 'Authorization' : `Bearer ${ idToken } ` } ;
0 commit comments