@shushed/helpers 0.0.36 → 0.0.38

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 CHANGED
@@ -28743,6 +28743,7 @@ declare class EnvEngine extends Runtime {
28743
28743
 
28744
28744
  type Message = {
28745
28745
  buildshipId: string;
28746
+ exportableToBigQuery: boolean;
28746
28747
  body: any;
28747
28748
  sourceSystem: string;
28748
28749
  targetSystem: string;
@@ -28750,6 +28751,7 @@ type Message = {
28750
28751
  recordModifiedAt: Date | null;
28751
28752
  originalPublishTime: Date | null;
28752
28753
  publishTime: Date | null;
28754
+ extraAttributes: Record<string, string>;
28753
28755
  };
28754
28756
  type LastModifiedKeys = 'last_modified_date_time' | 'last_modified_date' | 'last_modified_datetime' | 'record_modified_at' | 'lastModifiedDateTime' | 'lastModifiedAt' | 'last_modified_at' | 'SystemModifiedAt' | 'system_modified_at' | 'lastUpdatedAt' | 'modified_at' | 'last_updated_at' | 'modifiedAt';
28755
28757
  type AtLeastOne<T, Keys extends keyof T = keyof T> = Partial<T> & {
@@ -28792,6 +28794,7 @@ declare class PubSubHelper extends Runtime {
28792
28794
  private findTopic;
28793
28795
  private ensureTopicExists;
28794
28796
  private getNameFromFullyQualifiedName;
28797
+ private updateSubscription;
28795
28798
  private ensureSubscribtionExists;
28796
28799
  private ensureDLQSubscriptionExists;
28797
28800
  deleteSubscription(subscriptionURN: string): Promise<any>;
package/dist/index.js CHANGED
@@ -678,8 +678,13 @@ var env_default = EnvEngine;
678
678
 
679
679
  // src-public/pubsub.ts
680
680
  var import_co_body = __toESM(require("co-body"));
681
+ var import_lodash = __toESM(require("lodash.isequal"));
682
+ var import_lodash2 = __toESM(require("lodash.omit"));
683
+ function isSubscriptionOptionsPull(opts) {
684
+ return "endpointUrl" in opts && opts.endpoinrtUrl === null;
685
+ }
681
686
  function isSubscriptionOptionsPush(opts) {
682
- return "endpointUrl" in opts;
687
+ return "endpointUrl" in opts && opts.endpoinrtUrl !== null;
683
688
  }
684
689
  var PubSubHelper = class extends Runtime {
685
690
  constructor(opts, pubSub) {
@@ -705,7 +710,9 @@ var PubSubHelper = class extends Runtime {
705
710
  originalPublishTime: parseDateOrDefault(rawMessage.attributes?.original_publish_time, parseDateOrDefault(rawMessage.publish_time)),
706
711
  buildshipId: rawMessage.attributes?.buildship_id || "unknown",
707
712
  publishTime: parseDateOrDefault(rawMessage.publish_time),
708
- body: messageBody
713
+ body: messageBody,
714
+ exportableToBigQuery: !!rawMessage.attributes?.bigquery,
715
+ extraAttributes: (0, import_lodash2.default)(rawMessage.attributes || {}, ["source_system", "target_system", "process_started_at", "record_modified_at", "original_publish_time", "buildship_id", "publish_time", "bigquery"])
709
716
  };
710
717
  return message;
711
718
  }
@@ -726,16 +733,21 @@ var PubSubHelper = class extends Runtime {
726
733
  for (let i = 0; i < normPayloads.length; i++) {
727
734
  const normPayload = normPayloads[i];
728
735
  promises.push((async () => {
736
+ const attributes = {
737
+ ...(0, import_lodash2.default)(options.extraAttributes || {}, ["source_system", "target_system", "process_started_at", "record_modified_at", "original_publish_time", "buildship_id", "publish_time", "bigquery"]),
738
+ source_system: options.sourceSystem,
739
+ target_system: options.targetSystem || orgMessage?.targetSystem || "*",
740
+ process_started_at: (options.processStartedAt || orgMessage?.processStartedAt || /* @__PURE__ */ new Date()).toISOString(),
741
+ record_modified_at: (getEventTime(normPayload, null) || /* @__PURE__ */ new Date()).toISOString(),
742
+ original_publish_time: (orgMessage?.originalPublishTime || orgMessage?.publishTime || /* @__PURE__ */ new Date())?.toDateString() || "",
743
+ buildship_id: this.systemEnvName + "-" + this.workflowId + "-" + this.triggerId
744
+ };
745
+ if (options.exportableToBigQuery) {
746
+ attributes.bigquery = "yes";
747
+ }
729
748
  const result = await topic.publishMessage({
730
749
  data: Buffer.from(JSON.stringify(normPayload)),
731
- attributes: {
732
- source_system: options.sourceSystem,
733
- target_system: options.targetSystem || orgMessage?.targetSystem || "*",
734
- process_started_at: (options.processStartedAt || orgMessage?.processStartedAt || /* @__PURE__ */ new Date()).toISOString(),
735
- record_modified_at: (getEventTime(normPayload, null) || /* @__PURE__ */ new Date()).toISOString(),
736
- original_publish_time: (orgMessage?.originalPublishTime || orgMessage?.publishTime || /* @__PURE__ */ new Date())?.toDateString() || "",
737
- buildship_id: this.systemEnvName + "-" + this.workflowId + "-" + this.triggerId
738
- }
750
+ attributes
739
751
  });
740
752
  return result;
741
753
  })());
@@ -760,12 +772,20 @@ var PubSubHelper = class extends Runtime {
760
772
  let bqSubscription = null;
761
773
  if (opts.bigQueryTableId) {
762
774
  const bigQuerySubscriptionName = `${subscriptionName}-bq`;
775
+ const bigQueryDlqTopicName = `${opts.topicName}-bq-dlq`;
776
+ const bigQueryDlqSubscriptionName = `${subscriptionName}-bq-dlq`;
777
+ dlqTopic = await this.ensureTopicExists(bigQueryDlqTopicName);
778
+ dlqSubscription = await this.ensureDLQSubscriptionExists(bigQueryDlqSubscriptionName, {
779
+ topic: dlqTopic
780
+ });
763
781
  bqSubscription = await this.ensureSubscribtionExists(bigQuerySubscriptionName, {
764
782
  topic,
765
- tableId: opts.bigQueryTableId
783
+ tableId: opts.bigQueryTableId,
784
+ dlqTopic
766
785
  });
767
786
  } else {
768
787
  await this.deleteSubscription(`${subscriptionName}-bq`);
788
+ await this.deleteSubscription(`${subscriptionName}-bq-dlq`);
769
789
  }
770
790
  const subscription = await this.ensureSubscribtionExists(subscriptionName, {
771
791
  topic,
@@ -790,8 +810,46 @@ var PubSubHelper = class extends Runtime {
790
810
  }
791
811
  async ensureTopicExists(topicName) {
792
812
  let topic = await this.findTopic(topicName);
793
- if (!topic) {
794
- await this.pubSub.createTopic(topicName);
813
+ const metadata = {
814
+ labels: {
815
+ "created-at": (/* @__PURE__ */ new Date()).toISOString(),
816
+ "last-modified-at": (/* @__PURE__ */ new Date()).toISOString(),
817
+ "created-by-bs-triggerid": this.triggerId,
818
+ "created-by-bs-workflowid": this.workflowId,
819
+ "bs-envname": this.systemEnvName,
820
+ "env": this.envName,
821
+ "used-by": ""
822
+ }
823
+ };
824
+ const [topicMetadata] = await topic?.getMetadata() || [null];
825
+ const currentUsedBy = (topicMetadata?.labels?.["used-by"] || "").split(",").map((x) => x.split(":").slice(0, 2)).reduce((acc, [wid, envName]) => {
826
+ acc[wid] = acc[wid] || [];
827
+ acc[wid].push(`${envName}---${this.triggerId}`);
828
+ return acc;
829
+ }, {});
830
+ if (currentUsedBy[this.workflowId] && currentUsedBy[this.workflowId].indexOf(`${this.systemEnvName}---${this.triggerId}`) !== -1) {
831
+ currentUsedBy[this.workflowId].push(`${this.systemEnvName}---${this.triggerId}`);
832
+ }
833
+ if (topic) {
834
+ const currentUsedByString = Object.entries(currentUsedBy).reduce((acc, [wid, x]) => {
835
+ x.forEach((y) => {
836
+ acc.push(wid + ":" + y);
837
+ });
838
+ return acc;
839
+ }, []).join(",");
840
+ metadata.labels["used-by"] = currentUsedByString;
841
+ const simMetadata = Object.assign({}, topicMetadata?.labels || {}, metadata.labels);
842
+ if (!(0, import_lodash.default)(topicMetadata?.labels, simMetadata)) {
843
+ simMetadata["last-modified-at"] = (/* @__PURE__ */ new Date()).toISOString();
844
+ await topic.setMetadata({
845
+ labels: simMetadata
846
+ });
847
+ }
848
+ } else {
849
+ await this.pubSub.createTopic({
850
+ name: topicName,
851
+ labels: metadata.labels
852
+ });
795
853
  topic = await this.findTopic(topicName);
796
854
  }
797
855
  if (!topic) {
@@ -803,66 +861,119 @@ var PubSubHelper = class extends Runtime {
803
861
  const name = fullyQName.split("/");
804
862
  return name[name.length - 1];
805
863
  }
806
- async ensureSubscribtionExists(subscriptionName, opts) {
807
- let [subscriptions] = await opts.topic.getSubscriptions();
808
- let subscription = subscriptions.find((subscription2) => this.getNameFromFullyQualifiedName(subscription2.name) === subscriptionName);
809
- let endpointHasChanged = false;
810
- let retryLimitHasChanged = false;
811
- let ackDeadlineHasChanged = false;
812
- let retryDelayHasChanged = false;
813
- let shouldRecreate = false;
814
- let dlqHasChanged = false;
815
- let tableIdHasChanged = false;
816
- if (subscription) {
817
- const [subscriptionMetaData] = await subscription.getMetadata();
818
- if (!isSubscriptionOptionsPush(opts)) {
819
- tableIdHasChanged = (subscriptionMetaData.bigqueryConfig?.table || null) !== (opts.tableId || null);
820
- } else {
821
- endpointHasChanged = subscriptionMetaData.pushConfig?.pushEndpoint != opts.endpointUrl;
822
- dlqHasChanged = !subscriptionMetaData.deadLetterPolicy && !!opts.dlqTopic || !!subscriptionMetaData.deadLetterPolicy?.deadLetterTopic && !opts.dlqTopic;
823
- retryDelayHasChanged = subscriptionMetaData.retryPolicy?.minimumBackoff?.seconds != opts.retryMinDelay;
824
- retryLimitHasChanged = subscriptionMetaData.deadLetterPolicy?.maxDeliveryAttempts != opts.retryLimit;
825
- ackDeadlineHasChanged = subscriptionMetaData.ackDeadlineSeconds !== (opts.ackDeadline || 90);
864
+ async updateSubscription(subscriptionName, subscription, subscriptionMetaData, inputOpts) {
865
+ const defaultOptions = {
866
+ retryLimit: 5,
867
+ retryMinDelay: 10,
868
+ retryMaxDelay: 360,
869
+ ackDeadline: 90
870
+ };
871
+ const opts = Object.assign({}, defaultOptions, inputOpts);
872
+ let nextMetadata = null;
873
+ if (isSubscriptionOptionsPush(opts)) {
874
+ if (opts.endpointUrl !== subscriptionMetaData?.pushConfig?.pushEndpoint || this.serviceAccountEmail !== subscriptionMetaData?.pushConfig.oidcToken?.serviceAccountEmail || process.env.GCLOUD_PROJECT + "/" + this.triggerId !== subscriptionMetaData.pushConfig.oidcToken.audience) {
875
+ if (subscriptionMetaData?.pushConfig?.pushEndpoint && subscription) {
876
+ await subscription.modifyPushConfig({
877
+ pushEndpoint: opts.endpointUrl,
878
+ oidcToken: {
879
+ serviceAccountEmail: this.serviceAccountEmail,
880
+ audience: process.env.GCLOUD_PROJECT + "/" + this.triggerId
881
+ }
882
+ });
883
+ } else {
884
+ nextMetadata = nextMetadata || {};
885
+ nextMetadata.pushConfig = {
886
+ pushEndpoint: opts.endpointUrl,
887
+ oidcToken: {
888
+ serviceAccountEmail: this.serviceAccountEmail,
889
+ audience: process.env.GCLOUD_PROJECT + "/" + this.triggerId
890
+ }
891
+ };
892
+ }
826
893
  }
827
- shouldRecreate = endpointHasChanged || retryDelayHasChanged || retryLimitHasChanged || dlqHasChanged || tableIdHasChanged || ackDeadlineHasChanged;
828
- }
829
- if (subscription && shouldRecreate) {
830
- await subscription.delete();
831
- }
832
- const payload = {};
833
- if (!isSubscriptionOptionsPush(opts)) {
834
- payload.ackDeadlineSeconds = 60;
835
- payload.bigqueryConfig = {
836
- table: opts.tableId,
837
- writeMetadata: true,
838
- dropUnknownFields: true,
839
- serviceAccountEmail: this.serviceAccountEmail,
840
- useTableSchema: false
841
- };
842
894
  } else {
843
- payload.ackDeadlineSeconds = opts.ackDeadline || 90;
844
- payload.pushConfig = {
845
- pushEndpoint: opts.endpointUrl,
846
- oidcToken: {
847
- serviceAccountEmail: this.serviceAccountEmail,
848
- audience: process.env.GCLOUD_PROJECT + "/" + this.triggerId
849
- }
895
+ nextMetadata = nextMetadata || {};
896
+ nextMetadata.pushConfig = null;
897
+ }
898
+ if (opts.dlqTopic) {
899
+ if (opts.dlqTopic.name !== subscriptionMetaData?.deadLetterPolicy?.deadLetterTopic || opts.retryLimit !== subscriptionMetaData?.deadLetterPolicy.maxDeliveryAttempts) {
900
+ nextMetadata = nextMetadata || {};
901
+ nextMetadata.deadLetterPolicy = {
902
+ deadLetterTopic: opts.dlqTopic.name,
903
+ maxDeliveryAttempts: opts.retryLimit
904
+ };
905
+ }
906
+ } else if (subscriptionMetaData?.deadLetterPolicy?.deadLetterTopic) {
907
+ nextMetadata = nextMetadata || {
908
+ deadLetterPolicy: null
850
909
  };
851
- payload.retryPolicy = {
910
+ }
911
+ if (opts.retryMinDelay !== subscriptionMetaData?.retryPolicy?.minimumBackoff?.seconds || opts.retryMaxDelay !== subscriptionMetaData?.retryPolicy?.maximumBackoff?.seconds) {
912
+ nextMetadata = nextMetadata || {};
913
+ nextMetadata.retryPolicy = {
852
914
  minimumBackoff: { seconds: opts.retryMinDelay },
853
915
  maximumBackoff: { seconds: opts.retryMaxDelay }
854
916
  };
855
- if (opts.dlqTopic) {
856
- payload.deadLetterPolicy = {
857
- deadLetterTopic: this.getNameFromFullyQualifiedName(opts.dlqTopic.name),
858
- maxDeliveryAttempts: opts.retryLimit
859
- };
917
+ }
918
+ const proposedLabels = {
919
+ "created-at": subscriptionMetaData?.labels?.["created-at"] || (/* @__PURE__ */ new Date()).toISOString(),
920
+ "last-modified-at": subscriptionMetaData?.labels?.["created-at"] || (/* @__PURE__ */ new Date()).toISOString(),
921
+ "env": this.envName,
922
+ "bs-triggerid": this.triggerId,
923
+ "bs-workflowid": this.workflowId,
924
+ "bs-envname": this.systemEnvName,
925
+ "bs-runtimeurl": this.runtimeUrl,
926
+ "bs-is-deadletter": subscriptionName.indexOf("-dlq") > -1 ? "true" : "false",
927
+ "bs-is-push-enabled": isSubscriptionOptionsPush(opts) && opts.endpointUrl ? "true" : "false",
928
+ "bs-bigquery-tableid": !(isSubscriptionOptionsPush(opts) || isSubscriptionOptionsPull(opts)) && opts.tableId ? opts.tableId : "",
929
+ "bs-is-bigquery": !(isSubscriptionOptionsPush(opts) || isSubscriptionOptionsPull(opts)) && opts.tableId ? "true" : "false"
930
+ };
931
+ const simLabels = Object.assign({}, subscriptionMetaData?.labels || {}, proposedLabels);
932
+ if (!(0, import_lodash.default)(simLabels, subscriptionMetaData?.labels)) {
933
+ simLabels["last-modified-at"] = (/* @__PURE__ */ new Date()).toISOString();
934
+ nextMetadata = nextMetadata || {};
935
+ nextMetadata.labels = simLabels;
936
+ }
937
+ if (opts.ackDeadline !== subscriptionMetaData?.ackDeadlineSeconds) {
938
+ nextMetadata = nextMetadata || {};
939
+ nextMetadata.ackDeadlineSeconds = opts.ackDeadline;
940
+ }
941
+ if (!isSubscriptionOptionsPull(opts) && !isSubscriptionOptionsPush(opts)) {
942
+ if (opts.tableId !== subscriptionMetaData?.bigqueryConfig?.table || this.serviceAccountEmail !== subscriptionMetaData.bigqueryConfig?.serviceAccountEmail) {
943
+ if (opts.tableId) {
944
+ nextMetadata = nextMetadata || {};
945
+ nextMetadata.bigqueryConfig = {
946
+ table: opts.tableId,
947
+ writeMetadata: true,
948
+ dropUnknownFields: true,
949
+ serviceAccountEmail: this.serviceAccountEmail,
950
+ useTableSchema: false
951
+ };
952
+ nextMetadata.filter = `attributes:bigquery`;
953
+ } else {
954
+ nextMetadata = nextMetadata || {};
955
+ nextMetadata.bigqueryConfig = null;
956
+ }
860
957
  }
958
+ } else if (subscriptionMetaData?.bigqueryConfig?.table) {
959
+ nextMetadata = nextMetadata || {};
960
+ nextMetadata.bigqueryConfig = null;
861
961
  }
862
- if (!subscription || shouldRecreate) {
863
- await opts.topic.createSubscription(subscriptionName, payload);
962
+ if (subscription && nextMetadata) {
963
+ await subscription.setMetadata(nextMetadata);
964
+ return null;
965
+ }
966
+ return nextMetadata;
967
+ }
968
+ async ensureSubscribtionExists(subscriptionName, opts) {
969
+ let [subscriptions] = await opts.topic.getSubscriptions();
970
+ let subscription = subscriptions.find((subscription2) => this.getNameFromFullyQualifiedName(subscription2.name) === subscriptionName) || null;
971
+ const [subscriptionMetaData] = await subscription?.getMetadata() || [null];
972
+ const createPayload = await this.updateSubscription(subscriptionName, subscription, subscriptionMetaData, opts);
973
+ if (createPayload) {
974
+ await opts.topic.createSubscription(subscriptionName, createPayload);
864
975
  [subscriptions] = await opts.topic.getSubscriptions();
865
- subscription = subscriptions.find((subscription2) => this.getNameFromFullyQualifiedName(subscription2.name) === subscriptionName);
976
+ subscription = subscriptions.find((subscription2) => this.getNameFromFullyQualifiedName(subscription2.name) === subscriptionName) || null;
866
977
  }
867
978
  if (!subscription) {
868
979
  throw new Error(`Invariant: While creating subscription - subscription is not found`);
package/dist/package.json CHANGED
@@ -10,7 +10,10 @@
10
10
  "ajv-formats": "^3.0.1",
11
11
  "co-body": "^6.2.0",
12
12
  "jose": "^6.0.11",
13
- "lodash.clonedeep": "^4.5.0"
13
+ "lodash.clonedeep": "^4.5.0",
14
+ "lodash.isequal": "^4.5.0",
15
+ "lodash.omit": "^4.5.0",
16
+ "lodash.pick": "^4.4.0"
14
17
  },
15
18
  "files": [
16
19
  "dist"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shushed/helpers",
3
- "version": "0.0.36",
3
+ "version": "0.0.38",
4
4
  "author": "",
5
5
  "license": "UNLICENSED",
6
6
  "description": "",
@@ -10,7 +10,10 @@
10
10
  "ajv-formats": "^3.0.1",
11
11
  "co-body": "^6.2.0",
12
12
  "jose": "^6.0.11",
13
- "lodash.clonedeep": "^4.5.0"
13
+ "lodash.clonedeep": "^4.5.0",
14
+ "lodash.isequal": "^4.5.0",
15
+ "lodash.omit": "^4.5.0",
16
+ "lodash.pick": "^4.4.0"
14
17
  },
15
18
  "files": [
16
19
  "dist"