@shushed/helpers 0.0.21 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +157 -1
- package/dist/index.js +517 -1
- package/dist/package.json +1 -0
- package/package.json +2 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { JSONSchemaType } from 'ajv';
|
|
2
2
|
import { DeepRedact } from '@hackylabs/deep-redact';
|
|
3
|
+
import { DocumentReference, Firestore } from '@google-cloud/firestore';
|
|
4
|
+
import { PubSub, Topic, Subscription } from '@google-cloud/pubsub';
|
|
5
|
+
import * as _google_cloud_scheduler_build_protos_protos from '@google-cloud/scheduler/build/protos/protos';
|
|
6
|
+
import { CloudSchedulerClient } from '@google-cloud/scheduler';
|
|
7
|
+
import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
|
|
3
8
|
|
|
4
9
|
declare const schema$l: {
|
|
5
10
|
readonly $schema: "http://json-schema.org/draft-07/schema#";
|
|
@@ -28691,11 +28696,162 @@ declare function validate<T>(payload: T, schema: JSONSchemaType<T>, options: {
|
|
|
28691
28696
|
declare const sanitizeToString: DeepRedact;
|
|
28692
28697
|
declare const sanitize: DeepRedact;
|
|
28693
28698
|
|
|
28699
|
+
type Opts = {
|
|
28700
|
+
workflowId: string;
|
|
28701
|
+
triggerId: string;
|
|
28702
|
+
url: string;
|
|
28703
|
+
envName: string;
|
|
28704
|
+
} | {
|
|
28705
|
+
workflowId: string;
|
|
28706
|
+
triggerId: string;
|
|
28707
|
+
url: string;
|
|
28708
|
+
};
|
|
28709
|
+
declare class Runtime {
|
|
28710
|
+
readonly workflowId: string;
|
|
28711
|
+
readonly triggerId: string;
|
|
28712
|
+
readonly envName: string;
|
|
28713
|
+
readonly systemEnvName: string;
|
|
28714
|
+
readonly runtimeUrl: string;
|
|
28715
|
+
constructor(opts: Opts);
|
|
28716
|
+
}
|
|
28717
|
+
|
|
28718
|
+
declare class EnvEngine extends Runtime {
|
|
28719
|
+
docRef: DocumentReference;
|
|
28720
|
+
private store;
|
|
28721
|
+
constructor(opts: Opts, firestore: Firestore);
|
|
28722
|
+
set(envs: Array<{
|
|
28723
|
+
name: string;
|
|
28724
|
+
value: string;
|
|
28725
|
+
}>): Promise<undefined>;
|
|
28726
|
+
private initializeIfNeeded;
|
|
28727
|
+
get<T extends ReadonlyArray<string>>(keys: T): Promise<{
|
|
28728
|
+
[K in T[number]]: string;
|
|
28729
|
+
}>;
|
|
28730
|
+
get(key: string): Promise<string>;
|
|
28731
|
+
}
|
|
28732
|
+
|
|
28733
|
+
declare class PubSubHelper extends Runtime {
|
|
28734
|
+
private pubSub;
|
|
28735
|
+
constructor(opts: Opts, pubSub: PubSub);
|
|
28736
|
+
createOrUpdate(opts: {
|
|
28737
|
+
topicName: string;
|
|
28738
|
+
dlq: boolean;
|
|
28739
|
+
retryLimit: number;
|
|
28740
|
+
retryDelay: number;
|
|
28741
|
+
ackDeadline: number;
|
|
28742
|
+
}): Promise<{
|
|
28743
|
+
topic: Topic;
|
|
28744
|
+
dlqTopic: Topic | null;
|
|
28745
|
+
dlqSubscription: Subscription | null;
|
|
28746
|
+
subscription: Subscription;
|
|
28747
|
+
}>;
|
|
28748
|
+
private findTopic;
|
|
28749
|
+
private ensureTopicExists;
|
|
28750
|
+
private getNameFromFullyQualifiedName;
|
|
28751
|
+
private ensureSubscribtionExists;
|
|
28752
|
+
private ensureDLQSubscriptionExists;
|
|
28753
|
+
deleteSubscription(subscriptionURN: string): Promise<any>;
|
|
28754
|
+
}
|
|
28755
|
+
|
|
28756
|
+
declare class SchedulerHelper extends Runtime {
|
|
28757
|
+
private extraQuery;
|
|
28758
|
+
private pubSubTarget;
|
|
28759
|
+
private pubSubProjectId;
|
|
28760
|
+
private schedulerLocationId;
|
|
28761
|
+
private schedulerClient;
|
|
28762
|
+
constructor(opts: Opts & {
|
|
28763
|
+
extraQuery: Record<string, string>;
|
|
28764
|
+
serviceAccount: string;
|
|
28765
|
+
defaultServiceAccount: string;
|
|
28766
|
+
pubSubTarget?: Topic | null;
|
|
28767
|
+
schedulerLocationId?: string | null;
|
|
28768
|
+
}, schedulerClient: CloudSchedulerClient);
|
|
28769
|
+
get locationIds(): string[];
|
|
28770
|
+
getTarget(): {
|
|
28771
|
+
pubsubTarget: {
|
|
28772
|
+
topicName: string;
|
|
28773
|
+
data: Buffer<ArrayBuffer>;
|
|
28774
|
+
};
|
|
28775
|
+
httpTarget?: undefined;
|
|
28776
|
+
} | {
|
|
28777
|
+
httpTarget: {
|
|
28778
|
+
uri: string;
|
|
28779
|
+
httpMethod: "POST";
|
|
28780
|
+
headers: {
|
|
28781
|
+
"Content-Type": string;
|
|
28782
|
+
};
|
|
28783
|
+
oidcToken: {
|
|
28784
|
+
serviceAccountEmail: string;
|
|
28785
|
+
audience: string;
|
|
28786
|
+
};
|
|
28787
|
+
};
|
|
28788
|
+
pubsubTarget?: undefined;
|
|
28789
|
+
};
|
|
28790
|
+
create({ schedule, timezone }: {
|
|
28791
|
+
schedule: string;
|
|
28792
|
+
timezone: string;
|
|
28793
|
+
}): Promise<{
|
|
28794
|
+
status: _google_cloud_scheduler_build_protos_protos.google.rpc.IStatus | null | undefined;
|
|
28795
|
+
state: _google_cloud_scheduler_build_protos_protos.google.cloud.scheduler.v1.Job.State | "STATE_UNSPECIFIED" | "ENABLED" | "PAUSED" | "DISABLED" | "UPDATE_FAILED" | null | undefined;
|
|
28796
|
+
name: string | null | undefined;
|
|
28797
|
+
httpTarget: _google_cloud_scheduler_build_protos_protos.google.cloud.scheduler.v1.IHttpTarget | null | undefined;
|
|
28798
|
+
pubsubTarget: _google_cloud_scheduler_build_protos_protos.google.cloud.scheduler.v1.IPubsubTarget | null | undefined;
|
|
28799
|
+
}>;
|
|
28800
|
+
update({ schedule, timezone }: {
|
|
28801
|
+
schedule: string;
|
|
28802
|
+
timezone: string;
|
|
28803
|
+
}): Promise<{
|
|
28804
|
+
status: _google_cloud_scheduler_build_protos_protos.google.rpc.IStatus | null | undefined;
|
|
28805
|
+
state: _google_cloud_scheduler_build_protos_protos.google.cloud.scheduler.v1.Job.State | "STATE_UNSPECIFIED" | "ENABLED" | "PAUSED" | "DISABLED" | "UPDATE_FAILED" | null | undefined;
|
|
28806
|
+
name: string | null | undefined;
|
|
28807
|
+
httpTarget: _google_cloud_scheduler_build_protos_protos.google.cloud.scheduler.v1.IHttpTarget | null | undefined;
|
|
28808
|
+
pubsubTarget: _google_cloud_scheduler_build_protos_protos.google.cloud.scheduler.v1.IPubsubTarget | null | undefined;
|
|
28809
|
+
}>;
|
|
28810
|
+
delete(): Promise<any>;
|
|
28811
|
+
}
|
|
28812
|
+
|
|
28813
|
+
declare class Secrets extends Runtime {
|
|
28814
|
+
private secretsManagerClient;
|
|
28815
|
+
constructor(opts: Opts, secretsManagerClient: SecretManagerServiceClient);
|
|
28816
|
+
getSecret(name: string): Promise<string>;
|
|
28817
|
+
}
|
|
28818
|
+
|
|
28819
|
+
declare class JWKSHelper extends Runtime {
|
|
28820
|
+
private firestore;
|
|
28821
|
+
constructor(opts: Opts, firestore: Firestore);
|
|
28822
|
+
validateAuthorizationHeader(authorizationHeader: string, expectedServiceAccount?: string | null): Promise<boolean>;
|
|
28823
|
+
getKey(keyId: string): Promise<{
|
|
28824
|
+
kid: string;
|
|
28825
|
+
} | undefined>;
|
|
28826
|
+
private fetchJWKS;
|
|
28827
|
+
}
|
|
28828
|
+
|
|
28829
|
+
declare const isPubSubRequest: (headers: Record<string, string>) => boolean;
|
|
28830
|
+
declare const isCronMessage: (headers: Record<string, string>) => boolean;
|
|
28831
|
+
declare const validateGoogleAuth: (opts: Opts & {
|
|
28832
|
+
serviceAccount?: string | null;
|
|
28833
|
+
}, headers: Record<string, string>, firestore: Firestore) => Promise<void>;
|
|
28834
|
+
|
|
28835
|
+
type index_EnvEngine = EnvEngine;
|
|
28836
|
+
declare const index_EnvEngine: typeof EnvEngine;
|
|
28837
|
+
type index_JWKSHelper = JWKSHelper;
|
|
28838
|
+
declare const index_JWKSHelper: typeof JWKSHelper;
|
|
28839
|
+
type index_PubSubHelper = PubSubHelper;
|
|
28840
|
+
declare const index_PubSubHelper: typeof PubSubHelper;
|
|
28841
|
+
type index_Runtime = Runtime;
|
|
28842
|
+
declare const index_Runtime: typeof Runtime;
|
|
28843
|
+
type index_SchedulerHelper = SchedulerHelper;
|
|
28844
|
+
declare const index_SchedulerHelper: typeof SchedulerHelper;
|
|
28845
|
+
type index_Secrets = Secrets;
|
|
28846
|
+
declare const index_Secrets: typeof Secrets;
|
|
28847
|
+
declare const index_isCronMessage: typeof isCronMessage;
|
|
28848
|
+
declare const index_isPubSubRequest: typeof isPubSubRequest;
|
|
28694
28849
|
declare const index_sanitize: typeof sanitize;
|
|
28695
28850
|
declare const index_sanitizeToString: typeof sanitizeToString;
|
|
28696
28851
|
declare const index_validate: typeof validate;
|
|
28852
|
+
declare const index_validateGoogleAuth: typeof validateGoogleAuth;
|
|
28697
28853
|
declare namespace index {
|
|
28698
|
-
export { index_sanitize as sanitize, index_sanitizeToString as sanitizeToString, index_validate as validate };
|
|
28854
|
+
export { index_EnvEngine as EnvEngine, index_JWKSHelper as JWKSHelper, index_PubSubHelper as PubSubHelper, index_Runtime as Runtime, index_SchedulerHelper as SchedulerHelper, index_Secrets as Secrets, index_isCronMessage as isCronMessage, index_isPubSubRequest as isPubSubRequest, index_sanitize as sanitize, index_sanitizeToString as sanitizeToString, index_validate as validate, index_validateGoogleAuth as validateGoogleAuth };
|
|
28699
28855
|
}
|
|
28700
28856
|
|
|
28701
28857
|
export { index as lib, index$9 as schema, index$1 as types };
|
package/dist/index.js
CHANGED
|
@@ -280,9 +280,18 @@ var shipped_exports2 = {};
|
|
|
280
280
|
// src-public/index.ts
|
|
281
281
|
var src_public_exports = {};
|
|
282
282
|
__export(src_public_exports, {
|
|
283
|
+
EnvEngine: () => env_default,
|
|
284
|
+
JWKSHelper: () => jwks_default,
|
|
285
|
+
PubSubHelper: () => pubsub_default,
|
|
286
|
+
Runtime: () => Runtime,
|
|
287
|
+
SchedulerHelper: () => scheduler_default,
|
|
288
|
+
Secrets: () => secret_default,
|
|
289
|
+
isCronMessage: () => isCronMessage,
|
|
290
|
+
isPubSubRequest: () => isPubSubRequest,
|
|
283
291
|
sanitize: () => sanitize,
|
|
284
292
|
sanitizeToString: () => sanitizeToString,
|
|
285
|
-
validate: () => validate
|
|
293
|
+
validate: () => validate,
|
|
294
|
+
validateGoogleAuth: () => validateGoogleAuth
|
|
286
295
|
});
|
|
287
296
|
|
|
288
297
|
// src-public/validate.ts
|
|
@@ -336,6 +345,513 @@ var sanitize = new import_deep_redact.DeepRedact({
|
|
|
336
345
|
remove: false,
|
|
337
346
|
replaceStringByLength: true
|
|
338
347
|
});
|
|
348
|
+
|
|
349
|
+
// src-public/runtime.ts
|
|
350
|
+
var DEFAULT_BRANCH = "migratedprod";
|
|
351
|
+
var Runtime = class {
|
|
352
|
+
workflowId;
|
|
353
|
+
triggerId;
|
|
354
|
+
envName;
|
|
355
|
+
systemEnvName;
|
|
356
|
+
runtimeUrl;
|
|
357
|
+
constructor(opts) {
|
|
358
|
+
this.workflowId = opts.workflowId;
|
|
359
|
+
this.triggerId = opts.triggerId;
|
|
360
|
+
this.systemEnvName = typeof opts.envName !== "undefined" ? opts.envName : (() => {
|
|
361
|
+
const hostId = opts.url.replace(/^(?:https?:)?\/\//i, "").split("/")[0].split(".")[0].toLowerCase();
|
|
362
|
+
let branch = DEFAULT_BRANCH;
|
|
363
|
+
if (hostId.includes("-")) {
|
|
364
|
+
branch = hostId.split("-")[0];
|
|
365
|
+
}
|
|
366
|
+
return branch;
|
|
367
|
+
})();
|
|
368
|
+
this.runtimeUrl = "https://" + new URL("https://" + opts.url.replace(/^(?:https?:)?\/\//i, "").split("/")[0]).host;
|
|
369
|
+
this.envName = this.systemEnvName === DEFAULT_BRANCH ? "prod" : this.systemEnvName;
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
// src-public/env.ts
|
|
374
|
+
var EnvEngine = class extends Runtime {
|
|
375
|
+
docRef;
|
|
376
|
+
store = {};
|
|
377
|
+
constructor(opts, firestore) {
|
|
378
|
+
super(opts);
|
|
379
|
+
this.docRef = firestore?.doc(`${this.workflowId}/${this.triggerId}/${this.envName}`);
|
|
380
|
+
}
|
|
381
|
+
async set(envs) {
|
|
382
|
+
await this.initializeIfNeeded();
|
|
383
|
+
const obj = envs.filter((env) => env.name).reduce(
|
|
384
|
+
(prev, curr) => ({ ...prev, [curr.name]: curr.value }),
|
|
385
|
+
{}
|
|
386
|
+
);
|
|
387
|
+
if (this.docRef) {
|
|
388
|
+
try {
|
|
389
|
+
await this.docRef.set(
|
|
390
|
+
obj,
|
|
391
|
+
{ merge: true }
|
|
392
|
+
);
|
|
393
|
+
} catch (err) {
|
|
394
|
+
throw new Error(`Failed to set the ${envs.map((x) => x.name).join(", ")}. Error: ${err.message}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
Object.assign(this.store, obj);
|
|
398
|
+
return void 0;
|
|
399
|
+
}
|
|
400
|
+
async initializeIfNeeded() {
|
|
401
|
+
if (!this.store) {
|
|
402
|
+
if (this.docRef) {
|
|
403
|
+
const docSnapshot = await this.docRef.get();
|
|
404
|
+
const data = docSnapshot.data();
|
|
405
|
+
this.store = {};
|
|
406
|
+
for (const k in data) {
|
|
407
|
+
const v = data[k];
|
|
408
|
+
this.store[k] = typeof v !== "string" ? `${v === null || typeof v === "undefined" ? "" : `${v}`}` : v;
|
|
409
|
+
}
|
|
410
|
+
} else {
|
|
411
|
+
this.store = {};
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
async get(keys) {
|
|
416
|
+
await this.initializeIfNeeded();
|
|
417
|
+
if (typeof keys === "string") {
|
|
418
|
+
return this.store?.[keys] || "";
|
|
419
|
+
}
|
|
420
|
+
return keys.reduce((acc, k) => {
|
|
421
|
+
const v = this.store?.[k];
|
|
422
|
+
acc[k] = typeof v !== "string" ? `${v === null || typeof v === "undefined" ? "" : `${v}`}` : v;
|
|
423
|
+
return acc;
|
|
424
|
+
}, {});
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
var env_default = EnvEngine;
|
|
428
|
+
|
|
429
|
+
// src-public/pubsub.ts
|
|
430
|
+
var PubSubHelper = class extends Runtime {
|
|
431
|
+
constructor(opts, pubSub) {
|
|
432
|
+
super(opts);
|
|
433
|
+
this.pubSub = pubSub;
|
|
434
|
+
}
|
|
435
|
+
async createOrUpdate(opts) {
|
|
436
|
+
const subscriptionName = `${this.envName === "prod" ? "" : `${this.envName}---`}${this.triggerId}`;
|
|
437
|
+
const topic = await this.ensureTopicExists(opts.topicName);
|
|
438
|
+
let dlqTopic = null;
|
|
439
|
+
let dlqSubscription = null;
|
|
440
|
+
if (opts.dlq) {
|
|
441
|
+
const dlqTopicName = `${opts.topicName}-dlq`;
|
|
442
|
+
const dlqSubscriptionName = `${subscriptionName}-dlq`;
|
|
443
|
+
dlqTopic = await this.ensureTopicExists(dlqTopicName);
|
|
444
|
+
dlqSubscription = await this.ensureDLQSubscriptionExists(dlqSubscriptionName, {
|
|
445
|
+
topic: dlqTopic
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
const subscription = await this.ensureSubscribtionExists(subscriptionName, {
|
|
449
|
+
topic,
|
|
450
|
+
dlqTopic,
|
|
451
|
+
retryMinDelay: opts.retryDelay,
|
|
452
|
+
retryMaxDelay: opts.retryDelay,
|
|
453
|
+
retryLimit: opts.retryLimit,
|
|
454
|
+
endpointUrl: this.runtimeUrl + `/executeWorkflow/${this.workflowId}/${this.triggerId}`,
|
|
455
|
+
ackDeadline: opts.ackDeadline
|
|
456
|
+
});
|
|
457
|
+
return {
|
|
458
|
+
topic,
|
|
459
|
+
dlqTopic,
|
|
460
|
+
dlqSubscription,
|
|
461
|
+
subscription
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
async findTopic(topicName) {
|
|
465
|
+
const [topics] = await this.pubSub.getTopics();
|
|
466
|
+
return topics.find((topic) => this.getNameFromFullyQualifiedName(topic.name) === topicName);
|
|
467
|
+
}
|
|
468
|
+
async ensureTopicExists(topicName) {
|
|
469
|
+
let topic = await this.findTopic(topicName);
|
|
470
|
+
if (!topic) {
|
|
471
|
+
await this.pubSub.createTopic(topicName);
|
|
472
|
+
topic = await this.findTopic(topicName);
|
|
473
|
+
}
|
|
474
|
+
if (!topic) {
|
|
475
|
+
throw new Error(`Invariant: While creating topic - topic is not found`);
|
|
476
|
+
}
|
|
477
|
+
return topic;
|
|
478
|
+
}
|
|
479
|
+
getNameFromFullyQualifiedName(fullyQName) {
|
|
480
|
+
const name = fullyQName.split("/");
|
|
481
|
+
return name[name.length - 1];
|
|
482
|
+
}
|
|
483
|
+
async ensureSubscribtionExists(subscriptionName, opts) {
|
|
484
|
+
let [subscriptions] = await opts.topic.getSubscriptions();
|
|
485
|
+
let subscription = subscriptions.find((subscription2) => this.getNameFromFullyQualifiedName(subscription2.name) === subscriptionName);
|
|
486
|
+
let endpointHasChanged = false;
|
|
487
|
+
let retryLimitHasChanged = false;
|
|
488
|
+
let retryDelayHasChanged = false;
|
|
489
|
+
let shouldRecreate = false;
|
|
490
|
+
let dlqHasChanged = false;
|
|
491
|
+
if (subscription) {
|
|
492
|
+
const [subscriptionMetaData] = await subscription.getMetadata();
|
|
493
|
+
endpointHasChanged = subscriptionMetaData.pushConfig?.pushEndpoint != opts.endpointUrl;
|
|
494
|
+
retryDelayHasChanged = subscriptionMetaData.retryPolicy?.minimumBackoff?.seconds != opts.retryMinDelay;
|
|
495
|
+
retryLimitHasChanged = subscriptionMetaData.deadLetterPolicy?.maxDeliveryAttempts != opts.retryLimit;
|
|
496
|
+
dlqHasChanged = !subscriptionMetaData.deadLetterPolicy && !!opts.dlqTopic || !!subscriptionMetaData.deadLetterPolicy?.deadLetterTopic && !opts.dlqTopic;
|
|
497
|
+
shouldRecreate = endpointHasChanged || retryDelayHasChanged || retryLimitHasChanged || dlqHasChanged;
|
|
498
|
+
}
|
|
499
|
+
if (subscription && shouldRecreate) {
|
|
500
|
+
await subscription.delete();
|
|
501
|
+
}
|
|
502
|
+
if (!subscription || shouldRecreate) {
|
|
503
|
+
await opts.topic.createSubscription(subscriptionName, Object.assign({
|
|
504
|
+
ackDeadlineSeconds: opts.ackDeadline || 90,
|
|
505
|
+
pushConfig: {
|
|
506
|
+
pushEndpoint: opts.endpointUrl,
|
|
507
|
+
oidcToken: {
|
|
508
|
+
serviceAccountEmail: `runtime@${process.env.GCLOUD_PROJECT}.iam.gserviceaccount.com`,
|
|
509
|
+
audience: process.env.GCLOUD_PROJECT + "/" + this.triggerId
|
|
510
|
+
}
|
|
511
|
+
},
|
|
512
|
+
retryPolicy: {
|
|
513
|
+
minimumBackoff: { seconds: opts.retryMinDelay },
|
|
514
|
+
maximumBackoff: { seconds: opts.retryMaxDelay }
|
|
515
|
+
}
|
|
516
|
+
}, opts.dlqTopic ? {
|
|
517
|
+
deadLetterPolicy: {
|
|
518
|
+
deadLetterTopic: opts.dlqTopic.name,
|
|
519
|
+
maxDeliveryAttempts: opts.retryLimit
|
|
520
|
+
}
|
|
521
|
+
} : {}));
|
|
522
|
+
[subscriptions] = await opts.topic.getSubscriptions();
|
|
523
|
+
subscription = subscriptions.find((subscription2) => this.getNameFromFullyQualifiedName(subscription2.name) === subscriptionName);
|
|
524
|
+
}
|
|
525
|
+
if (!subscription) {
|
|
526
|
+
throw new Error(`Invariant: While creating subscription - subscription is not found`);
|
|
527
|
+
}
|
|
528
|
+
return subscription;
|
|
529
|
+
}
|
|
530
|
+
async ensureDLQSubscriptionExists(subscriptionName, opts) {
|
|
531
|
+
let [subscriptions] = await opts.topic.getSubscriptions();
|
|
532
|
+
let subscription = subscriptions.find((subscription2) => this.getNameFromFullyQualifiedName(subscription2.name) === subscriptionName);
|
|
533
|
+
if (!subscription) {
|
|
534
|
+
await opts.topic.createSubscription(subscriptionName);
|
|
535
|
+
[subscriptions] = await opts.topic.getSubscriptions();
|
|
536
|
+
subscription = subscriptions.find((subscription2) => this.getNameFromFullyQualifiedName(subscription2.name) === subscriptionName);
|
|
537
|
+
}
|
|
538
|
+
if (!subscription) {
|
|
539
|
+
throw new Error(`Invariant: While creating subscription - subscription is not found`);
|
|
540
|
+
}
|
|
541
|
+
return subscription;
|
|
542
|
+
}
|
|
543
|
+
async deleteSubscription(subscriptionURN) {
|
|
544
|
+
const sub = this.pubSub.subscription(subscriptionURN);
|
|
545
|
+
return sub.delete().catch((err) => {
|
|
546
|
+
if (err.code === 5) {
|
|
547
|
+
return err.message;
|
|
548
|
+
}
|
|
549
|
+
throw err;
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
var pubsub_default = PubSubHelper;
|
|
554
|
+
|
|
555
|
+
// src-public/scheduler.ts
|
|
556
|
+
var SchedulerHelper = class extends Runtime {
|
|
557
|
+
extraQuery;
|
|
558
|
+
pubSubTarget;
|
|
559
|
+
pubSubProjectId;
|
|
560
|
+
schedulerLocationId;
|
|
561
|
+
schedulerClient;
|
|
562
|
+
constructor(opts, schedulerClient) {
|
|
563
|
+
super(opts);
|
|
564
|
+
this.extraQuery = opts.extraQuery;
|
|
565
|
+
this.pubSubTarget = opts.pubSubTarget || null;
|
|
566
|
+
this.pubSubProjectId = this.pubSubTarget?.name?.split("/")[1] || null;
|
|
567
|
+
this.schedulerLocationId = opts.schedulerLocationId || "europe-west2";
|
|
568
|
+
this.schedulerClient = schedulerClient;
|
|
569
|
+
}
|
|
570
|
+
get locationIds() {
|
|
571
|
+
return [
|
|
572
|
+
"asia-east1",
|
|
573
|
+
"asia-northeast1",
|
|
574
|
+
"asia-southeast1",
|
|
575
|
+
"europe-west1",
|
|
576
|
+
"us-central1",
|
|
577
|
+
"us-east1",
|
|
578
|
+
"us-east4",
|
|
579
|
+
"us-west1"
|
|
580
|
+
];
|
|
581
|
+
}
|
|
582
|
+
getTarget() {
|
|
583
|
+
if (this.pubSubTarget) {
|
|
584
|
+
return {
|
|
585
|
+
pubsubTarget: {
|
|
586
|
+
topicName: this.pubSubTarget.name,
|
|
587
|
+
data: Buffer.from(JSON.stringify({
|
|
588
|
+
source: `${this.workflowId}/${this.triggerId}/${this.systemEnvName}`
|
|
589
|
+
}), "utf-8")
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
return {
|
|
594
|
+
httpTarget: {
|
|
595
|
+
uri: `${this.runtimeUrl}/executeWorkflow/${this.workflowId}/${this.triggerId}?${new URLSearchParams(this.extraQuery).toString()}`,
|
|
596
|
+
httpMethod: "POST",
|
|
597
|
+
headers: {
|
|
598
|
+
"Content-Type": "application/json"
|
|
599
|
+
},
|
|
600
|
+
oidcToken: {
|
|
601
|
+
serviceAccountEmail: `runtime@${process.env.GCLOUD_PROJECT}.iam.gserviceaccount.com`,
|
|
602
|
+
audience: process.env.GCLOUD_PROJECT + "/" + this.triggerId
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
async create({ schedule, timezone }) {
|
|
608
|
+
let parent;
|
|
609
|
+
let name;
|
|
610
|
+
if (this.pubSubProjectId) {
|
|
611
|
+
parent = `projects/${this.pubSubProjectId}/locations/${this.schedulerLocationId}`;
|
|
612
|
+
name = `${parent}/jobs/buildship-workflow-${this.triggerId}-${this.systemEnvName}`;
|
|
613
|
+
} else {
|
|
614
|
+
let locationId = process.env.SERVICE_REGION;
|
|
615
|
+
locationId = this.locationIds.includes(locationId) ? locationId : "us-central1";
|
|
616
|
+
parent = `projects/${process.env.GCLOUD_PROJECT}/locations/${locationId}`;
|
|
617
|
+
name = `${parent}/jobs/rowy-workflow-${this.triggerId}-${this.systemEnvName}`;
|
|
618
|
+
}
|
|
619
|
+
try {
|
|
620
|
+
const response = await this.schedulerClient.createJob({
|
|
621
|
+
parent,
|
|
622
|
+
job: {
|
|
623
|
+
name,
|
|
624
|
+
schedule,
|
|
625
|
+
timeZone: timezone,
|
|
626
|
+
...this.getTarget()
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
return {
|
|
630
|
+
status: response[0].status,
|
|
631
|
+
state: response[0].state,
|
|
632
|
+
name: response[0].name,
|
|
633
|
+
httpTarget: response[0].httpTarget,
|
|
634
|
+
pubsubTarget: response[0].pubsubTarget
|
|
635
|
+
};
|
|
636
|
+
} catch (error) {
|
|
637
|
+
if (error.code === 6) {
|
|
638
|
+
return this.update(
|
|
639
|
+
{ schedule, timezone }
|
|
640
|
+
);
|
|
641
|
+
} else {
|
|
642
|
+
throw error;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
async update({ schedule, timezone }) {
|
|
647
|
+
let parent;
|
|
648
|
+
let name;
|
|
649
|
+
if (this.pubSubProjectId) {
|
|
650
|
+
parent = `projects/${this.pubSubProjectId}/locations/${this.schedulerLocationId}`;
|
|
651
|
+
name = `${parent}/jobs/buildship-workflow-${this.triggerId}-${this.systemEnvName}`;
|
|
652
|
+
} else {
|
|
653
|
+
let locationId = process.env.SERVICE_REGION;
|
|
654
|
+
locationId = this.locationIds.includes(locationId) ? locationId : "us-central1";
|
|
655
|
+
parent = `projects/${process.env.GCLOUD_PROJECT}/locations/${locationId}`;
|
|
656
|
+
name = `${parent}/jobs/rowy-workflow-${this.triggerId}-${this.systemEnvName}`;
|
|
657
|
+
}
|
|
658
|
+
try {
|
|
659
|
+
const response = await this.schedulerClient.updateJob({
|
|
660
|
+
job: {
|
|
661
|
+
...this.getTarget(),
|
|
662
|
+
name,
|
|
663
|
+
schedule,
|
|
664
|
+
timeZone: timezone
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
return {
|
|
668
|
+
status: response[0].status,
|
|
669
|
+
state: response[0].state,
|
|
670
|
+
name: response[0].name,
|
|
671
|
+
httpTarget: response[0].httpTarget,
|
|
672
|
+
pubsubTarget: response[0].pubsubTarget
|
|
673
|
+
};
|
|
674
|
+
} catch {
|
|
675
|
+
const response = await this.schedulerClient.createJob({
|
|
676
|
+
parent,
|
|
677
|
+
job: {
|
|
678
|
+
name,
|
|
679
|
+
schedule,
|
|
680
|
+
timeZone: timezone,
|
|
681
|
+
...this.getTarget()
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
return {
|
|
685
|
+
status: response[0].status,
|
|
686
|
+
state: response[0].state,
|
|
687
|
+
name: response[0].name,
|
|
688
|
+
httpTarget: response[0].httpTarget,
|
|
689
|
+
pubsubTarget: response[0].pubsubTarget
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
async delete() {
|
|
694
|
+
let parent;
|
|
695
|
+
let name;
|
|
696
|
+
if (this.pubSubProjectId) {
|
|
697
|
+
parent = `projects/${this.pubSubProjectId}/locations/${this.schedulerLocationId}`;
|
|
698
|
+
name = `${parent}/jobs/buildship-workflow-${this.triggerId}-${this.systemEnvName}`;
|
|
699
|
+
} else {
|
|
700
|
+
let locationId = process.env.SERVICE_REGION;
|
|
701
|
+
locationId = this.locationIds.includes(locationId) ? locationId : "us-central1";
|
|
702
|
+
parent = `projects/${process.env.GCLOUD_PROJECT}/locations/${locationId}`;
|
|
703
|
+
name = `${parent}/jobs/rowy-workflow-${this.triggerId}-${this.systemEnvName}`;
|
|
704
|
+
}
|
|
705
|
+
const request = {
|
|
706
|
+
name
|
|
707
|
+
};
|
|
708
|
+
let response;
|
|
709
|
+
try {
|
|
710
|
+
response = await this.schedulerClient.deleteJob(request);
|
|
711
|
+
} catch (err) {
|
|
712
|
+
if (err.code === 5) {
|
|
713
|
+
return err.message;
|
|
714
|
+
}
|
|
715
|
+
throw err;
|
|
716
|
+
}
|
|
717
|
+
return response;
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
var scheduler_default = SchedulerHelper;
|
|
721
|
+
|
|
722
|
+
// src-public/secret.ts
|
|
723
|
+
var Secrets = class extends Runtime {
|
|
724
|
+
constructor(opts, secretsManagerClient) {
|
|
725
|
+
super(opts);
|
|
726
|
+
this.secretsManagerClient = secretsManagerClient;
|
|
727
|
+
}
|
|
728
|
+
async getSecret(name) {
|
|
729
|
+
try {
|
|
730
|
+
const [secretVersion] = await this.secretsManagerClient.accessSecretVersion({
|
|
731
|
+
name: `projects/${process.env.GCLOUD_PROJECT}/secrets/${this.systemEnvName}---${name}/versions/latest`
|
|
732
|
+
});
|
|
733
|
+
const payload = secretVersion.payload?.data?.toString() || "";
|
|
734
|
+
return payload;
|
|
735
|
+
} catch (err) {
|
|
736
|
+
throw new Error(`Failed to get secret ${name}. Error: ${err.message}`);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
var secret_default = Secrets;
|
|
741
|
+
|
|
742
|
+
// src-public/jwks.ts
|
|
743
|
+
var import_jose = require("jose");
|
|
744
|
+
var CACHE_TABLE = "cache-global";
|
|
745
|
+
var JWKSHelper = class extends Runtime {
|
|
746
|
+
constructor(opts, firestore) {
|
|
747
|
+
super(opts);
|
|
748
|
+
this.firestore = firestore;
|
|
749
|
+
}
|
|
750
|
+
async validateAuthorizationHeader(authorizationHeader, expectedServiceAccount = null) {
|
|
751
|
+
const splitted = authorizationHeader.split(".");
|
|
752
|
+
const keyMeta = JSON.parse(Buffer.from(splitted[0], "base64").toString("utf-8")) || {};
|
|
753
|
+
const meta = JSON.parse(Buffer.from(splitted[1], "base64").toString("utf-8")) || {};
|
|
754
|
+
if (!meta.email_verified) {
|
|
755
|
+
throw new Error("Authorization header - Email not verified");
|
|
756
|
+
}
|
|
757
|
+
if (expectedServiceAccount === null) {
|
|
758
|
+
expectedServiceAccount = `runtime@${process.env.GCLOUD_PROJECT}.iam.gserviceaccount.com`;
|
|
759
|
+
}
|
|
760
|
+
if (meta.email !== expectedServiceAccount) {
|
|
761
|
+
throw new Error(`Authorization header - Wrong service account, got ${meta?.email}, expected ${expectedServiceAccount}`);
|
|
762
|
+
}
|
|
763
|
+
if (!keyMeta.kid || !keyMeta.alg) {
|
|
764
|
+
throw new Error("Malformed Authorization Header. KID is missing");
|
|
765
|
+
}
|
|
766
|
+
const key = await this.getKey(keyMeta.kid);
|
|
767
|
+
if (!key) {
|
|
768
|
+
throw new Error("Incorrect Authorization Header. The key has not been found in the Google JWKS set");
|
|
769
|
+
}
|
|
770
|
+
const importedKey = await (0, import_jose.importJWK)(key, keyMeta.alg);
|
|
771
|
+
let validationCorrect = true;
|
|
772
|
+
try {
|
|
773
|
+
await (0, import_jose.jwtVerify)(authorizationHeader, importedKey, {
|
|
774
|
+
issuer: ["https://accounts.google.com", "accounts.google.com"],
|
|
775
|
+
clockTolerance: 0,
|
|
776
|
+
audience: process.env.GCLOUD_PROJECT + "/" + this.triggerId
|
|
777
|
+
});
|
|
778
|
+
} catch (err) {
|
|
779
|
+
validationCorrect = false;
|
|
780
|
+
}
|
|
781
|
+
if (!validationCorrect) {
|
|
782
|
+
throw new Error("Incorrect Authorization Header. The JWT is not valid");
|
|
783
|
+
}
|
|
784
|
+
return validationCorrect;
|
|
785
|
+
}
|
|
786
|
+
async getKey(keyId) {
|
|
787
|
+
const jwks = typeof global.jwks !== "undefined" ? global.jwks : null;
|
|
788
|
+
let parsedJwks = null;
|
|
789
|
+
if (jwks) {
|
|
790
|
+
try {
|
|
791
|
+
parsedJwks = JSON.parse(jwks);
|
|
792
|
+
} catch (err) {
|
|
793
|
+
err.message.toString();
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
let keyData = parsedJwks?.keys?.find((x) => x.kid === keyId);
|
|
797
|
+
let cachedItem;
|
|
798
|
+
if (!keyData) {
|
|
799
|
+
cachedItem = await this.firestore.collection(CACHE_TABLE).doc("google-jwks").get();
|
|
800
|
+
if (cachedItem.exists && cachedItem.data()?.expires_at.toDate() > /* @__PURE__ */ new Date()) {
|
|
801
|
+
keyData = cachedItem.data()?.value;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
keyData = parsedJwks?.keys.find((x) => x.kid === keyId);
|
|
805
|
+
if (!keyData) {
|
|
806
|
+
const result = await this.fetchJWKS();
|
|
807
|
+
parsedJwks = result.jwks;
|
|
808
|
+
if (cachedItem) {
|
|
809
|
+
cachedItem.ref.set({
|
|
810
|
+
value: parsedJwks,
|
|
811
|
+
expires_at: result.expiresAt
|
|
812
|
+
}, { merge: true });
|
|
813
|
+
}
|
|
814
|
+
keyData = parsedJwks?.keys.find((x) => x.kid === keyId);
|
|
815
|
+
}
|
|
816
|
+
if (parsedJwks && cachedItem) {
|
|
817
|
+
global.jwks = JSON.stringify(parsedJwks);
|
|
818
|
+
}
|
|
819
|
+
return keyData;
|
|
820
|
+
}
|
|
821
|
+
async fetchJWKS() {
|
|
822
|
+
const date = /* @__PURE__ */ new Date();
|
|
823
|
+
let expiresAt = new Date(Date.now() + 1e3 * 60 * 60);
|
|
824
|
+
const response = await fetch("https://www.googleapis.com/oauth2/v3/certs");
|
|
825
|
+
const maxAgePair = response.headers.get("Cache-Control")?.split(",").map((x) => x.trim().split("=")).find((x) => x[0] === "max-age");
|
|
826
|
+
if (maxAgePair && maxAgePair.length === 2) {
|
|
827
|
+
expiresAt = new Date(date.getTime() + parseInt(maxAgePair[1], 10) * 1e3);
|
|
828
|
+
}
|
|
829
|
+
const jwks = await response.json();
|
|
830
|
+
return {
|
|
831
|
+
expiresAt,
|
|
832
|
+
jwks
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
var jwks_default = JWKSHelper;
|
|
837
|
+
|
|
838
|
+
// src-public/utils.ts
|
|
839
|
+
var isPubSubRequest = (headers) => (headers["User-Agent"] || headers["user-agent"]) === "CloudPubSub-Google" || (headers["User-Agent"] || headers["user-agent"] || "").indexOf("APIs-Google") !== -1;
|
|
840
|
+
var isCronMessage = (headers) => (headers["User-Agent"] || headers["user-agent"]) === "Google-Cloud-Scheduler";
|
|
841
|
+
var validateGoogleAuth = async (opts, headers, firestore) => {
|
|
842
|
+
const token = headers.authorization?.split(" ")[1];
|
|
843
|
+
if (!token) {
|
|
844
|
+
throw new Error(
|
|
845
|
+
"Missing authorization header or invalid format, needs to be in format: Bearer <IdToken>"
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
try {
|
|
849
|
+
const jwksHelper = new jwks_default(opts, firestore);
|
|
850
|
+
await jwksHelper.validateAuthorizationHeader(token, opts.serviceAccount || null);
|
|
851
|
+
} catch (err) {
|
|
852
|
+
throw new Error("Invalid authorization header " + err.message);
|
|
853
|
+
}
|
|
854
|
+
};
|
|
339
855
|
// Annotate the CommonJS export names for ESM import in node:
|
|
340
856
|
0 && (module.exports = {
|
|
341
857
|
lib,
|
package/dist/package.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shushed/helpers",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.22",
|
|
4
4
|
"author": "",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"description": "",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"@hackylabs/deep-redact": "^2.2.1",
|
|
9
9
|
"ajv": "^8.17.1",
|
|
10
10
|
"ajv-formats": "^3.0.1",
|
|
11
|
+
"jose": "^6.0.11",
|
|
11
12
|
"lodash.clonedeep": "^4.5.0"
|
|
12
13
|
},
|
|
13
14
|
"files": [
|