@stackone/olap 1.5.0 → 1.7.0

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.cjs CHANGED
@@ -1 +1 @@
1
- let e=require(`@stackone/redaction`),t=require(`@stackone/utils`),n=require(`@aws-sdk/client-s3`),r=require(`date-fns`),i=require(`kafkajs`);const a=e=>{try{return JSON.stringify(e)}catch{return`[Unserializable payload]`}},o=[`x-datadog-parent-id`,`x-datadog-sampling-priority`,`x-datadog-tags`,`x-datadog-trace-id`,`x-forwarded-proto`,`x-forwarded-port`,`x-forwarded-for`,`x-amzn-trace-id`,`traceparent`,`tracestate`,`x-request-nonce`,`x-signing-method`,`x-signature`,`host`,`via`],s=[`application/octet-stream`,`application/pdf`,`application/msword`,`application/vnd.openxmlformats-officedocument.wordprocessingml.document`,`application/vnd.ms-excel`,`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`,`application/zip`,`application/x-zip-compressed`],c=[`image/`,`audio/`,`video/`,`application/vnd.`,`binary`],l=10*1024*1024;var u=class{#e;constructor({logger:e}={}){this.#e=e}async write(e,t,n,r){try{let i=r?.ttl??30,a=await this.generateS3Command(t,n,i);await e.send(a)}catch(e){throw this.#e?.error({message:`Failed to write advanced logs to S3`,category:`AdvancedWriter`,code:`AdvancedWriteError`,error:e}),e}}generateS3Key(e,t,n,r){let i=`${t}/${n}/${e}`;return r?`${i}/steps/${r}.json`:`${i}/${e}.json`}async generateS3Command(e,t,i){let a=process.env.ADVANCED_LOGS_BUCKET;if(!a)throw Error(`ADVANCED_LOGS_BUCKET environment variable is not set`);let o=this.generateS3Key(t.actionRunId,t.organizationId,t.projectSecureId),s=(0,r.getUnixTime)((0,r.addDays)(new Date,i)),c=this.serialize(e,s,10485760),l=`ttl=30d`;return i===1?l=`ttl=1d`:i===7&&(l=`ttl=7d`),new n.PutObjectCommand({Bucket:a,Key:o,Body:c,ContentType:`application/json`,Expires:new Date(s*1e3),Tagging:l})}filterHeaders(e){if(!e)return;let t={},n=o.map(e=>e.toLowerCase());for(let[r,i]of Object.entries(e))n.includes(r.toLowerCase())||(t[r]=i);return t}isDataSyncRequest(e){return e?.mode===`data_sync`}isBackgroundLog(e){return(0,t.notMissing)(e.mode)&&[`refresh_authentication`].includes(e.mode)}getContentType(e){if(!e)return;let t=[`content-type`,`contenttype`];return Object.entries(e).find(([e])=>t.includes(e.toLowerCase()))?.[1]?.toString()}serialize(e,n,r){let i=e.response?.body;((0,t.isString)(i)&&i.length>r||Buffer.isBuffer(i)&&i.byteLength>r||i&&(0,t.isObject)(i)&&!Array.isArray(i)&&Object.values(i).some(e=>(0,t.isString)(e)&&e.length>r||Buffer.isBuffer(e)&&e.byteLength>r))&&(e={...e,response:{...e.response,body:{error:`Error.TOO_LARGE`}}});let a=JSON.stringify({data:e,expirationTime:n});if(Buffer.byteLength(a,`utf8`)>r){let t={...e,response:{...e.response,body:{error:`Error.TOO_LARGE`}}};a=JSON.stringify({data:t,expirationTime:n})}return a}processContent(e,t,n){let r=this.getContentType(e);if(!r)return a(t);let i=r.toLowerCase().split(`;`)[0].trim();return s.includes(i)||c.some(e=>i.startsWith(e))?{type:`binary`,action:n,contentType:r}:a(t)}};const d=[`x-datadog-parent-id`,`x-datadog-sampling-priority`,`x-datadog-tags`,`x-datadog-trace-id`,`x-forwarded-proto`,`x-forwarded-port`,`x-forwarded-for`,`x-amzn-trace-id`,`traceparent`,`tracestate`,`x-request-nonce`,`x-signing-method`,`x-signature`,`host`,`via`],f=10*1024*1024;var p=class{#e;#t;#n;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n??new u({logger:t})}async initialize(){if(!this.#e){this.#t?.warning({message:`No s3 client provided, advanced sink will not function`,category:`AdvancedSink`});return}}async send(e,t,r){if(!r?.enabled){this.#t?.debug({message:`Advanced sink is disabled, skipping sending to advanced sink`,category:`AdvancedSink`});return}if(!this.#e){this.#t?.warning({message:`No s3 client available, cannot send to advanced sink`,category:`AdvancedSink`});return}if(`mode`in t&&this.isDataSyncRequest(t)){this.#t?.debug({message:`Data sync request detected, skipping advanced log storage for actionRunId ${t.actionRunId}`,category:`AdvancedSink`});return}try{let i=await this.createS3Item(e,t,r.ttl);await this.#e.send(new n.PutObjectCommand({Bucket:i.Bucket,Key:i.Key,Body:i.Body,ContentType:i.ContentType,Expires:i.Expires,Tagging:i.Tagging}))}catch(e){throw this.#t?.error({message:`Failed to write advanced logs to S3 account ${t.accountSecureId}`,error:e,code:`AdvancedLogWriteError`,category:`AdvancedSink`,context:{actionRunId:t.actionRunId}}),e}}async createS3Item(e,t,n){let{projectSecureId:r,organizationId:i}=t,a=`ttl=30d`;typeof n==`number`&&(n===1?a=`ttl=1d`:n===7&&(a=`ttl=7d`));let o=Math.floor(Date.now()/1e3)+(n??30)*24*60*60,s=process.env.ADVANCED_LOGS_BUCKET;if(!s)throw Error(`ADVANCED_LOGS_BUCKET environment variable is not set`);return{Bucket:s,Key:this.generateS3Key(t,i,r),Body:this.serialize(e,t,o,10485760),ContentType:`application/json`,Expires:new Date(o*1e3),Tagging:a}}generateS3Key(e,t,n){return`actionId`in e?`${t}/${n}/${e.actionRunId}/${e.actionRunId}.json`:`${t}/${n}/${e.actionRunId}/steps/${e.stepId}.json`}serialize(e,t,n,r){return`actionId`in t?JSON.stringify(this.serializeActionResult(e,t,n,r)):JSON.stringify(this.serializeStepResult(e,t))}serializeActionResult(t,n,r,i){let a=(0,e.redactObject)({req:{...t.headers&&{headers:{...t.headers}}},res:{...n.headers&&{headers:{...n.headers}}}},e.CensorType.PARTIAL),o=a.req?.headers,s=a.res?.headers,c=(0,e.redactObject)(t.body,e.CensorType.PARTIAL),l=(0,e.redactObject)(n.body,e.CensorType.PARTIAL),u=this.isBackgroundLog(n);return{data:{request:{id:n.actionRunId,actionId:n.actionId,method:n.httpMethod,headers:this.filterHeaders(o),url:{url:t.url,path:t.pathParams,queryParams:t.queryParams},body:c},response:{statusCode:this.getStatusCode(n.status),headers:this.filterHeaders(s),body:l},...u?{isBackgroundLog:!0}:{}},metadata:{...u?{isBackgroundLog:!0}:{},expirationTime:r},expirationTime:r}}serializeStepResult(e,t){return{}}getStatusCode(e){let t=parseInt(e??``,10);return isNaN(t)?e===`success`?200:500:t}filterHeaders(e){if(!e)return;let t={},n=d.map(e=>e.toLowerCase());for(let[r,i]of Object.entries(e))n.includes(r.toLowerCase())||(t[r]=i);return t}isDataSyncRequest(e){return e?.mode===`data_sync`}isBackgroundLog(e){return(0,t.notMissing)(e.mode)&&[`refresh_authentication`].includes(e.mode)}getContentType(e){if(!e)return;let t=[`content-type`,`contenttype`];return Object.entries(e).find(([e])=>t.includes(e.toLowerCase()))?.[1]?.toString()}};const m=(e,t)=>{try{return new n.S3Client(e)??void 0}catch(e){let n=e;t?.error({message:`Error building s3 client: ${n.message}`,error:n,code:`BuildS3ClientError`,category:`buildS3Client`});return}},h=(e,t)=>{try{return new i.Kafka(e)??void 0}catch(e){let n=e;t?.error({message:`Error building kafka client: ${n.message}`,error:n,code:`BuildKafkaClientError`,category:`buildKafkaClient`});return}};var g=class{#e;#t;#n;constructor(e,t){this.#e=e,this.#n=t}async initialize(){if(!this.#e){this.#n?.warning({message:`No kafka client provided, logs sink cannot be initialized`,category:`LogsSink`});return}if(!this.#t){try{this.#t=this.#e.producer({createPartitioner:i.Partitioners.DefaultPartitioner})}catch(e){this.#n?.error({message:`Failed to create kafka producer for logs sink`,code:`KafkaProducerCreationError`,category:`LogsSink`,error:e}),this.#t=void 0;return}try{await this.#t.connect()}catch(e){this.#n?.error?.({message:`Failed to connect kafka producer for logs sink`,code:`KafkaProducerConnectionError`,category:`LogsSink`,error:e}),this.#t=void 0;return}this.#n?.info({message:`Logs sink kafka producer initialized`,category:`LogsSink`})}}async send(e,t,n){if(!n?.enabled){this.#n?.debug({message:`Logs sink is disabled, skipping send to topic ${e}`,category:`KafkaSink`});return}if(!this.#t)throw this.#n?.error({message:`Kafka not initialized, dropping message for topic ${e}`,category:`KafkaSink`,code:`KafkaNotReady`}),Error(`Kafka client is not initialized`);let r=a(this.buildLog(t));this.#n?.debug({message:`Sending to topic ${e}: ${r}`,category:`KafkaSink`});try{let t=await this.#t.send({topic:e,messages:[{value:r}]});this.#n?.debug({message:`Kafka producer response: ${JSON.stringify(t)}`,category:`KafkaSink`})}catch(t){throw this.#n?.error({message:`Error sending to topic ${e}: ${t.message}`,category:`KafkaSink`,error:t,code:`KafkaSendError`}),t}}buildLog(e){if(this.isActionResult(e))return Object.fromEntries(Object.entries({actionRunId:e.actionRunId,actionId:e.actionId,organizationId:e.organizationId,projectSecureId:e.projectSecureId,accountSecureId:e.accountSecureId,mode:e.mode,connectorKey:e.connectorKey,connectorVersion:e.connectorVersion,actionType:e.actionType,category:e.category,originOwnerId:e.originOwnerId,originOwnerName:e.originOwnerName,httpMethod:e.httpMethod,url:e.url,sourceId:e.sourceId,sourceType:e.sourceType,sourceValue:e.sourceValue,status:e.status,startTime:e.startTime,endTime:e.endTime,durationMs:this.calculateDuration(e.startTime,e.endTime),eventTime:new Date}).filter(([,e])=>e!==void 0));if(this.isStepResult(e))return Object.fromEntries(Object.entries({actionRunId:e.actionRunId,stepId:e.stepId,skipped:e.skipped,status:e.status,message:e.message,startTime:e.startTime,endTime:e.endTime,durationMs:this.calculateDuration(e.startTime,e.endTime),eventTime:new Date}).filter(([,e])=>e!==void 0));throw this.#n?.error({message:`Unknown result type, cannot build log`,category:`KafkaSink`,code:`UnknownResultType`}),Error(`Unknown result type`)}calculateDuration(e,t){if(!(e instanceof Date)||!(t instanceof Date))return;let n=e.getTime(),r=t.getTime();if(!(Number.isNaN(n)||Number.isNaN(r)||r<n))return r-n}isActionResult(e){return typeof e==`object`&&!!e&&`actionRunId`in e&&`actionId`in e}isStepResult(e){return typeof e==`object`&&!!e&&`actionRunId`in e&&`stepId`in e}};const _={logs:{enabled:!0},advanced:{enabled:!1,ttl:7,errorsOnly:!1}};function v(e){return{..._,...e}}var y=class{#e;#t;#n;#r;#i;constructor({getKafkaClient:e=h,getS3Client:t=m,kafkaClientConfig:n,s3ClientConfig:r,logger:i,advancedWriter:a}={}){this.name=`OlapClient`,this.#e=e(n,i),this.#t=t(r,i),this.#n=i,this.#r=new g(this.#e,this.#n),this.#i=new p(this.#t,this.#n,a)}async initialize(){await this.#r?.initialize(),await this.#i?.initialize()}async recordAction(e,t,n){let{logs:r,advanced:i}=v(n);if(r?.enabled)try{await this.#r?.send(`actions`,t,r)}catch(e){this.#n?.warning({message:`[OlapClient] Error sending to kafka: ${e.message}`,category:`OlapClient`,error:e})}if(i?.enabled)try{await this.#i?.send(e,t,i)}catch(e){this.#n?.warning({message:`[OlapClient] Error sending to s3: ${e.message}`,category:`OlapClient`,error:e})}}async recordStep(e,t,n){let{logs:r,advanced:i}=v(n);if(r?.enabled)try{await this.#r?.send(`steps`,t,r)}catch(e){this.#n?.warning({message:`[OlapClient] Error sending to kafka: ${e.message}`,category:`OlapClient`,error:e})}if(i?.enabled)try{await this.#i?.send(e,t,i)}catch(e){this.#n?.warning({message:`[OlapClient] Error sending to s3: ${e.message}`,category:`OlapClient`,error:e})}}};const b=async({getKafkaClient:e=h,getS3Client:t=m,kafkaClientConfig:n,s3ClientConfig:r,logger:i}={})=>{let a=new y({getKafkaClient:e,getS3Client:t,kafkaClientConfig:n,s3ClientConfig:r,logger:i});return await a.initialize(),a};var x=class{static{this.olapClientPromise=null}static getInstance({getKafkaClient:e,getS3Client:t,kafkaClientConfig:n,s3ClientConfig:r,logger:i,getOlapClient:a=b}={}){return this.olapClientPromise||=a({getKafkaClient:e,getS3Client:t,kafkaClientConfig:n,s3ClientConfig:r,logger:i}),this.olapClientPromise}static resetInstance(){this.olapClientPromise=null}};exports.OlapClient=y,exports.OlapClientManager=x;
1
+ let e=require(`@stackone/redaction`),t=require(`@stackone/utils`),n=require(`@aws-sdk/client-s3`),r=require(`kafkajs`);const i=[`x-datadog-parent-id`,`x-datadog-sampling-priority`,`x-datadog-tags`,`x-datadog-trace-id`,`x-forwarded-proto`,`x-forwarded-port`,`x-forwarded-for`,`x-amzn-trace-id`,`traceparent`,`tracestate`,`x-request-nonce`,`x-signing-method`,`x-signature`,`host`,`via`],a=10*1024*1024;var o=class{#e;#t;constructor(e,t){this.#e=e,this.#t=t}async initialize(){if(!this.#e){this.#t?.warning({message:`No s3 client provided, advanced sink will not function`,category:`AdvancedSink`});return}}async send(e,t,r){if(!r?.enabled){this.#t?.debug({message:`Advanced sink is disabled, skipping sending to advanced sink`,category:`AdvancedSink`});return}if(!this.#e){this.#t?.warning({message:`No s3 client available, cannot send to advanced sink`,category:`AdvancedSink`});return}if(`mode`in e&&this.isDataSyncRequest(e)){this.#t?.debug({message:`Data sync request detected, skipping advanced log storage for actionRunId ${t.actionRunId}`,category:`AdvancedSink`});return}try{let i=await this.createS3Item(e,t,r.ttl);await this.#e.send(new n.PutObjectCommand({Bucket:i.Bucket,Key:i.Key,Body:i.Body,ContentType:i.ContentType,Expires:i.Expires,Tagging:i.Tagging}))}catch(e){throw this.#t?.error({message:`Failed to write advanced logs to S3 account ${t.accountSecureId}`,error:e,code:`AdvancedLogWriteError`,category:`AdvancedSink`,context:{actionRunId:t.actionRunId}}),e}}async createS3Item(e,t,n){let{projectSecureId:r,organizationId:i}=t,a=`ttl=30d`;typeof n==`number`&&(n===1?a=`ttl=1d`:n===7&&(a=`ttl=7d`));let o=Math.floor(Date.now()/1e3)+(n??30)*24*60*60,s=process.env.ADVANCED_LOGS_BUCKET;if(!s)throw Error(`ADVANCED_LOGS_BUCKET environment variable is not set`);return{Bucket:s,Key:this.generateS3Key(t,i,r),Body:this.serialize(e,t,o,10485760),ContentType:`application/json`,Expires:new Date(o*1e3),Tagging:a}}generateS3Key(e,t,n){return`actionId`in e?`${t}/${n}/${e.actionRunId}/${e.actionRunId}.json`:`${t}/${n}/${e.actionRunId}/steps/${e.stepId}.json`}serialize(e,t,n,r){return`actionId`in t?JSON.stringify(this.serializeActionResult(e,t,n,r)):JSON.stringify(this.serializeStepResult(e,t))}serializeActionResult(t,n,r,i){let a=(0,e.redactObject)({req:{...t.headers&&{headers:{...t.headers}}},res:{...n.headers&&{headers:{...n.headers}}}},e.CensorType.PARTIAL),o=a.req?.headers,s=a.res?.headers,c=(0,e.redactObject)(t.body,e.CensorType.PARTIAL),l=(0,e.redactObject)(n.body,e.CensorType.PARTIAL),u=this.isBackgroundLog(t);return{data:{request:{id:n.actionRunId,actionId:n.actionId,method:n.httpMethod,headers:this.filterHeaders(o),url:{url:t.url,path:t.pathParams,queryParams:t.queryParams},body:c},response:{statusCode:n.statusCode??500,headers:this.filterHeaders(s),body:l},...u?{isBackgroundLog:!0}:{}},metadata:{...u?{isBackgroundLog:!0}:{},expirationTime:r},expirationTime:r}}serializeStepResult(e,t){return{}}filterHeaders(e){if(!e)return;let t={},n=i.map(e=>e.toLowerCase());for(let[r,i]of Object.entries(e))n.includes(r.toLowerCase())||(t[r]=i);return t}isDataSyncRequest(e){return e.mode===`data_sync`}isBackgroundLog(e){return(0,t.notMissing)(e.mode)&&[`refresh_authentication`].includes(e.mode)}getContentType(e){if(!e)return;let t=[`content-type`,`contenttype`];return Object.entries(e).find(([e])=>t.includes(e.toLowerCase()))?.[1]?.toString()}};const s=(e,t)=>{try{return new n.S3Client(e)??void 0}catch(e){let n=e;t?.error({message:`Error building s3 client: ${n.message}`,error:n,code:`BuildS3ClientError`,category:`buildS3Client`});return}},c=(e,t)=>{try{return new r.Kafka(e)??void 0}catch(e){let n=e;t?.error({message:`Error building kafka client: ${n.message}`,error:n,code:`BuildKafkaClientError`,category:`buildKafkaClient`});return}},l=e=>{try{return JSON.stringify(e)}catch{return`[Unserializable payload]`}};var u=class{#e;#t;#n;constructor(e,t){this.#e=e,this.#n=t}async initialize(){if(!this.#e){this.#n?.warning({message:`No kafka client provided, logs sink cannot be initialized`,category:`LogsSink`});return}if(!this.#t){try{this.#t=this.#e.producer({createPartitioner:r.Partitioners.DefaultPartitioner})}catch(e){this.#n?.error({message:`Failed to create kafka producer for logs sink`,code:`KafkaProducerCreationError`,category:`LogsSink`,error:e}),this.#t=void 0;return}try{await this.#t.connect()}catch(e){this.#n?.error?.({message:`Failed to connect kafka producer for logs sink`,code:`KafkaProducerConnectionError`,category:`LogsSink`,error:e}),this.#t=void 0;return}this.#n?.info({message:`Logs sink kafka producer initialized`,category:`LogsSink`})}}async send(e,t,n,r){if(!r?.enabled){this.#n?.debug({message:`Logs sink is disabled, skipping sending to log sink`,category:`KafkaSink`});return}if(!this.#t)throw this.#n?.error({message:`Kafka not initialized, dropping message for topic ${e}`,category:`KafkaSink`,code:`KafkaNotReady`}),Error(`Kafka client is not initialized`);let i=l(this.buildLog(t,n));this.#n?.debug({message:`Sending to topic ${e}: ${i}`,category:`KafkaSink`});try{let t=await this.#t.send({topic:e,messages:[{value:i}]});this.#n?.debug({message:`Kafka producer response: ${JSON.stringify(t)}`,category:`KafkaSink`})}catch(t){throw this.#n?.error({message:`Error sending to topic ${e}: ${t.message}`,category:`KafkaSink`,error:t,code:`KafkaSendError`}),t}}buildLog(e,t){if(this.isActionResult(t))return Object.fromEntries(Object.entries({actionRunId:t.actionRunId,actionId:t.actionId,organizationId:t.organizationId,projectSecureId:t.projectSecureId,accountSecureId:t.accountSecureId,mode:e.mode,connectorKey:t.connectorKey,connectorVersion:t.connectorVersion,actionType:t.actionType,category:t.category,originOwnerId:t.originOwnerId,originOwnerName:t.originOwnerName,httpMethod:t.httpMethod,url:t.url,sourceId:e.sourceId,sourceType:e.sourceType,sourceValue:e.sourceValue,success:t.success,statusCode:t.statusCode,startTime:t.startTime,endTime:t.endTime,durationMs:this.calculateDuration(t.startTime,t.endTime),eventTime:new Date}).filter(([,e])=>e!==void 0));if(this.isStepResult(t))return Object.fromEntries(Object.entries({actionRunId:t.actionRunId,stepId:t.stepId,skipped:t.skipped,status:t.status,message:t.message,startTime:t.startTime,endTime:t.endTime,durationMs:this.calculateDuration(t.startTime,t.endTime),eventTime:new Date}).filter(([,e])=>e!==void 0));throw this.#n?.error({message:`Unknown result type, cannot build log`,category:`KafkaSink`,code:`UnknownResultType`}),Error(`Unknown result type`)}calculateDuration(e,t){if(!(e instanceof Date)||!(t instanceof Date))return;let n=e.getTime(),r=t.getTime();if(!(Number.isNaN(n)||Number.isNaN(r)||r<n))return r-n}isActionResult(e){return typeof e==`object`&&!!e&&`actionRunId`in e&&`actionId`in e}isStepResult(e){return typeof e==`object`&&!!e&&`actionRunId`in e&&`stepId`in e}};const d={logs:{enabled:!0},advanced:{enabled:!1,ttl:7,errorsOnly:!1}};function f(e){return{...d,...e}}var p=class{#e;#t;#n;#r;#i;constructor({getKafkaClient:e=c,getS3Client:t=s,kafkaClientConfig:n,s3ClientConfig:r,logger:i}={}){this.name=`OlapClient`,this.#e=e(n,i),this.#t=t(r,i),this.#n=i,this.#r=new u(this.#e,this.#n),this.#i=new o(this.#t,this.#n)}async initialize(){await this.#r?.initialize(),await this.#i?.initialize()}async recordAction(e,t,n){let{logs:r,advanced:i}=f(n);if(r?.enabled)try{await this.#r?.send(`actions`,e,t,r)}catch(e){this.#n?.warning({message:`[OlapClient] Error sending to kafka: ${e.message}`,category:`OlapClient`,error:e})}if(i?.enabled)try{await this.#i?.send(e,t,i)}catch(e){this.#n?.warning({message:`[OlapClient] Error sending to s3: ${e.message}`,category:`OlapClient`,error:e})}}async recordStep(e,t,n){let{logs:r,advanced:i}=f(n);if(r?.enabled)try{await this.#r?.send(`steps`,e,t,r)}catch(e){this.#n?.warning({message:`[OlapClient] Error sending to kafka: ${e.message}`,category:`OlapClient`,error:e})}if(i?.enabled)try{await this.#i?.send(e,t,i)}catch(e){this.#n?.warning({message:`[OlapClient] Error sending to s3: ${e.message}`,category:`OlapClient`,error:e})}}};const m=async({getKafkaClient:e=c,getS3Client:t=s,kafkaClientConfig:n,s3ClientConfig:r,logger:i}={})=>{let a=new p({getKafkaClient:e,getS3Client:t,kafkaClientConfig:n,s3ClientConfig:r,logger:i});return await a.initialize(),a};var h=class{static{this.olapClientPromise=null}static getInstance({getKafkaClient:e,getS3Client:t,kafkaClientConfig:n,s3ClientConfig:r,logger:i,getOlapClient:a=m}={}){return this.olapClientPromise||=a({getKafkaClient:e,getS3Client:t,kafkaClientConfig:n,s3ClientConfig:r,logger:i}),this.olapClientPromise}static resetInstance(){this.olapClientPromise=null}};exports.OlapClient=p,exports.OlapClientManager=h;
package/dist/index.d.cts CHANGED
@@ -85,11 +85,15 @@ interface IOlapClient {
85
85
  }
86
86
  type ActionInput = {
87
87
  actionId?: string;
88
+ mode?: string;
88
89
  url?: string;
89
90
  pathParams?: unknown;
90
91
  queryParams?: unknown;
91
92
  body?: unknown;
92
93
  headers?: Record<string, string>;
94
+ sourceId?: string;
95
+ sourceType?: string;
96
+ sourceValue?: string;
93
97
  };
94
98
  type ActionResult = {
95
99
  actionRunId: string;
@@ -97,7 +101,6 @@ type ActionResult = {
97
101
  connectorKey: string;
98
102
  connectorVersion: string;
99
103
  actionType: string;
100
- mode?: string;
101
104
  category?: string;
102
105
  organizationId: string;
103
106
  projectSecureId: string;
@@ -107,13 +110,11 @@ type ActionResult = {
107
110
  httpMethod?: string;
108
111
  url?: string;
109
112
  path?: string;
110
- sourceId?: string;
111
- sourceType?: string;
112
- sourceValue?: string;
113
113
  resource?: string;
114
114
  subResource?: string;
115
115
  childResource?: string;
116
- status?: string;
116
+ success?: boolean;
117
+ statusCode?: number;
117
118
  message?: string;
118
119
  headers?: Record<string, string>;
119
120
  body?: unknown;
@@ -143,45 +144,6 @@ interface S3ClientConfig {
143
144
  region?: string;
144
145
  }
145
146
  type S3ClientBuilder = (config?: S3ClientConfig, logger?: ILogger) => S3Client | undefined;
146
- interface AdvancedLog {
147
- request: {
148
- id?: string;
149
- method?: string;
150
- headers?: Record<string, unknown>;
151
- url: {
152
- url: string;
153
- hostname: string;
154
- path: string;
155
- queryParams: Record<string, string>;
156
- };
157
- body?: string | Record<string, unknown>;
158
- };
159
- response: {
160
- statusCode?: number;
161
- headers?: Record<string, unknown>;
162
- body?: unknown;
163
- };
164
- isBackgroundLog?: boolean;
165
- }
166
- //#endregion
167
- //#region src/advanced/advancedWriter.d.ts
168
- declare class AdvancedWriter {
169
- #private;
170
- constructor({
171
- logger
172
- }?: {
173
- logger?: ILogger;
174
- });
175
- write(s3Client: S3Client, logData: AdvancedLog, result: ActionResult, options: AdvancedOptions): Promise<void>;
176
- private generateS3Key;
177
- private generateS3Command;
178
- private filterHeaders;
179
- private isDataSyncRequest;
180
- private isBackgroundLog;
181
- private getContentType;
182
- private serialize;
183
- private processContent;
184
- }
185
147
  //#endregion
186
148
  //#region src/logs/types.d.ts
187
149
  interface KafkaClientConfig {
@@ -198,15 +160,13 @@ declare class OlapClient implements IOlapClient {
198
160
  getS3Client,
199
161
  kafkaClientConfig,
200
162
  s3ClientConfig,
201
- logger,
202
- advancedWriter
163
+ logger
203
164
  }?: {
204
165
  getKafkaClient?: KafkaClientBuilder;
205
166
  getS3Client?: S3ClientBuilder;
206
167
  kafkaClientConfig?: KafkaClientConfig;
207
168
  s3ClientConfig?: S3ClientConfig;
208
169
  logger?: ILogger;
209
- advancedWriter?: AdvancedWriter;
210
170
  });
211
171
  initialize(): Promise<void>;
212
172
  recordAction(actionInput: ActionInput, actionResult: ActionResult, options?: OlapOptions): Promise<void>;
package/dist/index.d.mts CHANGED
@@ -85,11 +85,15 @@ interface IOlapClient {
85
85
  }
86
86
  type ActionInput = {
87
87
  actionId?: string;
88
+ mode?: string;
88
89
  url?: string;
89
90
  pathParams?: unknown;
90
91
  queryParams?: unknown;
91
92
  body?: unknown;
92
93
  headers?: Record<string, string>;
94
+ sourceId?: string;
95
+ sourceType?: string;
96
+ sourceValue?: string;
93
97
  };
94
98
  type ActionResult = {
95
99
  actionRunId: string;
@@ -97,7 +101,6 @@ type ActionResult = {
97
101
  connectorKey: string;
98
102
  connectorVersion: string;
99
103
  actionType: string;
100
- mode?: string;
101
104
  category?: string;
102
105
  organizationId: string;
103
106
  projectSecureId: string;
@@ -107,13 +110,11 @@ type ActionResult = {
107
110
  httpMethod?: string;
108
111
  url?: string;
109
112
  path?: string;
110
- sourceId?: string;
111
- sourceType?: string;
112
- sourceValue?: string;
113
113
  resource?: string;
114
114
  subResource?: string;
115
115
  childResource?: string;
116
- status?: string;
116
+ success?: boolean;
117
+ statusCode?: number;
117
118
  message?: string;
118
119
  headers?: Record<string, string>;
119
120
  body?: unknown;
@@ -143,45 +144,6 @@ interface S3ClientConfig {
143
144
  region?: string;
144
145
  }
145
146
  type S3ClientBuilder = (config?: S3ClientConfig, logger?: ILogger) => S3Client | undefined;
146
- interface AdvancedLog {
147
- request: {
148
- id?: string;
149
- method?: string;
150
- headers?: Record<string, unknown>;
151
- url: {
152
- url: string;
153
- hostname: string;
154
- path: string;
155
- queryParams: Record<string, string>;
156
- };
157
- body?: string | Record<string, unknown>;
158
- };
159
- response: {
160
- statusCode?: number;
161
- headers?: Record<string, unknown>;
162
- body?: unknown;
163
- };
164
- isBackgroundLog?: boolean;
165
- }
166
- //#endregion
167
- //#region src/advanced/advancedWriter.d.ts
168
- declare class AdvancedWriter {
169
- #private;
170
- constructor({
171
- logger
172
- }?: {
173
- logger?: ILogger;
174
- });
175
- write(s3Client: S3Client, logData: AdvancedLog, result: ActionResult, options: AdvancedOptions): Promise<void>;
176
- private generateS3Key;
177
- private generateS3Command;
178
- private filterHeaders;
179
- private isDataSyncRequest;
180
- private isBackgroundLog;
181
- private getContentType;
182
- private serialize;
183
- private processContent;
184
- }
185
147
  //#endregion
186
148
  //#region src/logs/types.d.ts
187
149
  interface KafkaClientConfig {
@@ -198,15 +160,13 @@ declare class OlapClient implements IOlapClient {
198
160
  getS3Client,
199
161
  kafkaClientConfig,
200
162
  s3ClientConfig,
201
- logger,
202
- advancedWriter
163
+ logger
203
164
  }?: {
204
165
  getKafkaClient?: KafkaClientBuilder;
205
166
  getS3Client?: S3ClientBuilder;
206
167
  kafkaClientConfig?: KafkaClientConfig;
207
168
  s3ClientConfig?: S3ClientConfig;
208
169
  logger?: ILogger;
209
- advancedWriter?: AdvancedWriter;
210
170
  });
211
171
  initialize(): Promise<void>;
212
172
  recordAction(actionInput: ActionInput, actionResult: ActionResult, options?: OlapOptions): Promise<void>;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{CensorType as e,redactObject as t}from"@stackone/redaction";import{isObject as n,isString as r,notMissing as i}from"@stackone/utils";import{PutObjectCommand as a,S3Client as o}from"@aws-sdk/client-s3";import{addDays as s,getUnixTime as c}from"date-fns";import{Kafka as l,Partitioners as u}from"kafkajs";const d=e=>{try{return JSON.stringify(e)}catch{return`[Unserializable payload]`}},f=[`x-datadog-parent-id`,`x-datadog-sampling-priority`,`x-datadog-tags`,`x-datadog-trace-id`,`x-forwarded-proto`,`x-forwarded-port`,`x-forwarded-for`,`x-amzn-trace-id`,`traceparent`,`tracestate`,`x-request-nonce`,`x-signing-method`,`x-signature`,`host`,`via`],p=[`application/octet-stream`,`application/pdf`,`application/msword`,`application/vnd.openxmlformats-officedocument.wordprocessingml.document`,`application/vnd.ms-excel`,`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`,`application/zip`,`application/x-zip-compressed`],m=[`image/`,`audio/`,`video/`,`application/vnd.`,`binary`];var h=class{#e;constructor({logger:e}={}){this.#e=e}async write(e,t,n,r){try{let i=r?.ttl??30,a=await this.generateS3Command(t,n,i);await e.send(a)}catch(e){throw this.#e?.error({message:`Failed to write advanced logs to S3`,category:`AdvancedWriter`,code:`AdvancedWriteError`,error:e}),e}}generateS3Key(e,t,n,r){let i=`${t}/${n}/${e}`;return r?`${i}/steps/${r}.json`:`${i}/${e}.json`}async generateS3Command(e,t,n){let r=process.env.ADVANCED_LOGS_BUCKET;if(!r)throw Error(`ADVANCED_LOGS_BUCKET environment variable is not set`);let i=this.generateS3Key(t.actionRunId,t.organizationId,t.projectSecureId),o=c(s(new Date,n)),l=this.serialize(e,o,10485760),u=`ttl=30d`;return n===1?u=`ttl=1d`:n===7&&(u=`ttl=7d`),new a({Bucket:r,Key:i,Body:l,ContentType:`application/json`,Expires:new Date(o*1e3),Tagging:u})}filterHeaders(e){if(!e)return;let t={},n=f.map(e=>e.toLowerCase());for(let[r,i]of Object.entries(e))n.includes(r.toLowerCase())||(t[r]=i);return t}isDataSyncRequest(e){return e?.mode===`data_sync`}isBackgroundLog(e){return i(e.mode)&&[`refresh_authentication`].includes(e.mode)}getContentType(e){if(!e)return;let t=[`content-type`,`contenttype`];return Object.entries(e).find(([e])=>t.includes(e.toLowerCase()))?.[1]?.toString()}serialize(e,t,i){let a=e.response?.body;(r(a)&&a.length>i||Buffer.isBuffer(a)&&a.byteLength>i||a&&n(a)&&!Array.isArray(a)&&Object.values(a).some(e=>r(e)&&e.length>i||Buffer.isBuffer(e)&&e.byteLength>i))&&(e={...e,response:{...e.response,body:{error:`Error.TOO_LARGE`}}});let o=JSON.stringify({data:e,expirationTime:t});if(Buffer.byteLength(o,`utf8`)>i){let n={...e,response:{...e.response,body:{error:`Error.TOO_LARGE`}}};o=JSON.stringify({data:n,expirationTime:t})}return o}processContent(e,t,n){let r=this.getContentType(e);if(!r)return d(t);let i=r.toLowerCase().split(`;`)[0].trim();return p.includes(i)||m.some(e=>i.startsWith(e))?{type:`binary`,action:n,contentType:r}:d(t)}};const g=[`x-datadog-parent-id`,`x-datadog-sampling-priority`,`x-datadog-tags`,`x-datadog-trace-id`,`x-forwarded-proto`,`x-forwarded-port`,`x-forwarded-for`,`x-amzn-trace-id`,`traceparent`,`tracestate`,`x-request-nonce`,`x-signing-method`,`x-signature`,`host`,`via`];var _=class{#e;#t;#n;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n??new h({logger:t})}async initialize(){if(!this.#e){this.#t?.warning({message:`No s3 client provided, advanced sink will not function`,category:`AdvancedSink`});return}}async send(e,t,n){if(!n?.enabled){this.#t?.debug({message:`Advanced sink is disabled, skipping sending to advanced sink`,category:`AdvancedSink`});return}if(!this.#e){this.#t?.warning({message:`No s3 client available, cannot send to advanced sink`,category:`AdvancedSink`});return}if(`mode`in t&&this.isDataSyncRequest(t)){this.#t?.debug({message:`Data sync request detected, skipping advanced log storage for actionRunId ${t.actionRunId}`,category:`AdvancedSink`});return}try{let r=await this.createS3Item(e,t,n.ttl);await this.#e.send(new a({Bucket:r.Bucket,Key:r.Key,Body:r.Body,ContentType:r.ContentType,Expires:r.Expires,Tagging:r.Tagging}))}catch(e){throw this.#t?.error({message:`Failed to write advanced logs to S3 account ${t.accountSecureId}`,error:e,code:`AdvancedLogWriteError`,category:`AdvancedSink`,context:{actionRunId:t.actionRunId}}),e}}async createS3Item(e,t,n){let{projectSecureId:r,organizationId:i}=t,a=`ttl=30d`;typeof n==`number`&&(n===1?a=`ttl=1d`:n===7&&(a=`ttl=7d`));let o=Math.floor(Date.now()/1e3)+(n??30)*24*60*60,s=process.env.ADVANCED_LOGS_BUCKET;if(!s)throw Error(`ADVANCED_LOGS_BUCKET environment variable is not set`);return{Bucket:s,Key:this.generateS3Key(t,i,r),Body:this.serialize(e,t,o,10485760),ContentType:`application/json`,Expires:new Date(o*1e3),Tagging:a}}generateS3Key(e,t,n){return`actionId`in e?`${t}/${n}/${e.actionRunId}/${e.actionRunId}.json`:`${t}/${n}/${e.actionRunId}/steps/${e.stepId}.json`}serialize(e,t,n,r){return`actionId`in t?JSON.stringify(this.serializeActionResult(e,t,n,r)):JSON.stringify(this.serializeStepResult(e,t))}serializeActionResult(n,r,i,a){let o=t({req:{...n.headers&&{headers:{...n.headers}}},res:{...r.headers&&{headers:{...r.headers}}}},e.PARTIAL),s=o.req?.headers,c=o.res?.headers,l=t(n.body,e.PARTIAL),u=t(r.body,e.PARTIAL),d=this.isBackgroundLog(r);return{data:{request:{id:r.actionRunId,actionId:r.actionId,method:r.httpMethod,headers:this.filterHeaders(s),url:{url:n.url,path:n.pathParams,queryParams:n.queryParams},body:l},response:{statusCode:this.getStatusCode(r.status),headers:this.filterHeaders(c),body:u},...d?{isBackgroundLog:!0}:{}},metadata:{...d?{isBackgroundLog:!0}:{},expirationTime:i},expirationTime:i}}serializeStepResult(e,t){return{}}getStatusCode(e){let t=parseInt(e??``,10);return isNaN(t)?e===`success`?200:500:t}filterHeaders(e){if(!e)return;let t={},n=g.map(e=>e.toLowerCase());for(let[r,i]of Object.entries(e))n.includes(r.toLowerCase())||(t[r]=i);return t}isDataSyncRequest(e){return e?.mode===`data_sync`}isBackgroundLog(e){return i(e.mode)&&[`refresh_authentication`].includes(e.mode)}getContentType(e){if(!e)return;let t=[`content-type`,`contenttype`];return Object.entries(e).find(([e])=>t.includes(e.toLowerCase()))?.[1]?.toString()}};const v=(e,t)=>{try{return new o(e)??void 0}catch(e){let n=e;t?.error({message:`Error building s3 client: ${n.message}`,error:n,code:`BuildS3ClientError`,category:`buildS3Client`});return}},y=(e,t)=>{try{return new l(e)??void 0}catch(e){let n=e;t?.error({message:`Error building kafka client: ${n.message}`,error:n,code:`BuildKafkaClientError`,category:`buildKafkaClient`});return}};var b=class{#e;#t;#n;constructor(e,t){this.#e=e,this.#n=t}async initialize(){if(!this.#e){this.#n?.warning({message:`No kafka client provided, logs sink cannot be initialized`,category:`LogsSink`});return}if(!this.#t){try{this.#t=this.#e.producer({createPartitioner:u.DefaultPartitioner})}catch(e){this.#n?.error({message:`Failed to create kafka producer for logs sink`,code:`KafkaProducerCreationError`,category:`LogsSink`,error:e}),this.#t=void 0;return}try{await this.#t.connect()}catch(e){this.#n?.error?.({message:`Failed to connect kafka producer for logs sink`,code:`KafkaProducerConnectionError`,category:`LogsSink`,error:e}),this.#t=void 0;return}this.#n?.info({message:`Logs sink kafka producer initialized`,category:`LogsSink`})}}async send(e,t,n){if(!n?.enabled){this.#n?.debug({message:`Logs sink is disabled, skipping send to topic ${e}`,category:`KafkaSink`});return}if(!this.#t)throw this.#n?.error({message:`Kafka not initialized, dropping message for topic ${e}`,category:`KafkaSink`,code:`KafkaNotReady`}),Error(`Kafka client is not initialized`);let r=d(this.buildLog(t));this.#n?.debug({message:`Sending to topic ${e}: ${r}`,category:`KafkaSink`});try{let t=await this.#t.send({topic:e,messages:[{value:r}]});this.#n?.debug({message:`Kafka producer response: ${JSON.stringify(t)}`,category:`KafkaSink`})}catch(t){throw this.#n?.error({message:`Error sending to topic ${e}: ${t.message}`,category:`KafkaSink`,error:t,code:`KafkaSendError`}),t}}buildLog(e){if(this.isActionResult(e))return Object.fromEntries(Object.entries({actionRunId:e.actionRunId,actionId:e.actionId,organizationId:e.organizationId,projectSecureId:e.projectSecureId,accountSecureId:e.accountSecureId,mode:e.mode,connectorKey:e.connectorKey,connectorVersion:e.connectorVersion,actionType:e.actionType,category:e.category,originOwnerId:e.originOwnerId,originOwnerName:e.originOwnerName,httpMethod:e.httpMethod,url:e.url,sourceId:e.sourceId,sourceType:e.sourceType,sourceValue:e.sourceValue,status:e.status,startTime:e.startTime,endTime:e.endTime,durationMs:this.calculateDuration(e.startTime,e.endTime),eventTime:new Date}).filter(([,e])=>e!==void 0));if(this.isStepResult(e))return Object.fromEntries(Object.entries({actionRunId:e.actionRunId,stepId:e.stepId,skipped:e.skipped,status:e.status,message:e.message,startTime:e.startTime,endTime:e.endTime,durationMs:this.calculateDuration(e.startTime,e.endTime),eventTime:new Date}).filter(([,e])=>e!==void 0));throw this.#n?.error({message:`Unknown result type, cannot build log`,category:`KafkaSink`,code:`UnknownResultType`}),Error(`Unknown result type`)}calculateDuration(e,t){if(!(e instanceof Date)||!(t instanceof Date))return;let n=e.getTime(),r=t.getTime();if(!(Number.isNaN(n)||Number.isNaN(r)||r<n))return r-n}isActionResult(e){return typeof e==`object`&&!!e&&`actionRunId`in e&&`actionId`in e}isStepResult(e){return typeof e==`object`&&!!e&&`actionRunId`in e&&`stepId`in e}};const x={logs:{enabled:!0},advanced:{enabled:!1,ttl:7,errorsOnly:!1}};function S(e){return{...x,...e}}var C=class{#e;#t;#n;#r;#i;constructor({getKafkaClient:e=y,getS3Client:t=v,kafkaClientConfig:n,s3ClientConfig:r,logger:i,advancedWriter:a}={}){this.name=`OlapClient`,this.#e=e(n,i),this.#t=t(r,i),this.#n=i,this.#r=new b(this.#e,this.#n),this.#i=new _(this.#t,this.#n,a)}async initialize(){await this.#r?.initialize(),await this.#i?.initialize()}async recordAction(e,t,n){let{logs:r,advanced:i}=S(n);if(r?.enabled)try{await this.#r?.send(`actions`,t,r)}catch(e){this.#n?.warning({message:`[OlapClient] Error sending to kafka: ${e.message}`,category:`OlapClient`,error:e})}if(i?.enabled)try{await this.#i?.send(e,t,i)}catch(e){this.#n?.warning({message:`[OlapClient] Error sending to s3: ${e.message}`,category:`OlapClient`,error:e})}}async recordStep(e,t,n){let{logs:r,advanced:i}=S(n);if(r?.enabled)try{await this.#r?.send(`steps`,t,r)}catch(e){this.#n?.warning({message:`[OlapClient] Error sending to kafka: ${e.message}`,category:`OlapClient`,error:e})}if(i?.enabled)try{await this.#i?.send(e,t,i)}catch(e){this.#n?.warning({message:`[OlapClient] Error sending to s3: ${e.message}`,category:`OlapClient`,error:e})}}};const w=async({getKafkaClient:e=y,getS3Client:t=v,kafkaClientConfig:n,s3ClientConfig:r,logger:i}={})=>{let a=new C({getKafkaClient:e,getS3Client:t,kafkaClientConfig:n,s3ClientConfig:r,logger:i});return await a.initialize(),a};var T=class{static{this.olapClientPromise=null}static getInstance({getKafkaClient:e,getS3Client:t,kafkaClientConfig:n,s3ClientConfig:r,logger:i,getOlapClient:a=w}={}){return this.olapClientPromise||=a({getKafkaClient:e,getS3Client:t,kafkaClientConfig:n,s3ClientConfig:r,logger:i}),this.olapClientPromise}static resetInstance(){this.olapClientPromise=null}};export{C as OlapClient,T as OlapClientManager};
1
+ import{CensorType as e,redactObject as t}from"@stackone/redaction";import{notMissing as n}from"@stackone/utils";import{PutObjectCommand as r,S3Client as i}from"@aws-sdk/client-s3";import{Kafka as a,Partitioners as o}from"kafkajs";const s=[`x-datadog-parent-id`,`x-datadog-sampling-priority`,`x-datadog-tags`,`x-datadog-trace-id`,`x-forwarded-proto`,`x-forwarded-port`,`x-forwarded-for`,`x-amzn-trace-id`,`traceparent`,`tracestate`,`x-request-nonce`,`x-signing-method`,`x-signature`,`host`,`via`];var c=class{#e;#t;constructor(e,t){this.#e=e,this.#t=t}async initialize(){if(!this.#e){this.#t?.warning({message:`No s3 client provided, advanced sink will not function`,category:`AdvancedSink`});return}}async send(e,t,n){if(!n?.enabled){this.#t?.debug({message:`Advanced sink is disabled, skipping sending to advanced sink`,category:`AdvancedSink`});return}if(!this.#e){this.#t?.warning({message:`No s3 client available, cannot send to advanced sink`,category:`AdvancedSink`});return}if(`mode`in e&&this.isDataSyncRequest(e)){this.#t?.debug({message:`Data sync request detected, skipping advanced log storage for actionRunId ${t.actionRunId}`,category:`AdvancedSink`});return}try{let i=await this.createS3Item(e,t,n.ttl);await this.#e.send(new r({Bucket:i.Bucket,Key:i.Key,Body:i.Body,ContentType:i.ContentType,Expires:i.Expires,Tagging:i.Tagging}))}catch(e){throw this.#t?.error({message:`Failed to write advanced logs to S3 account ${t.accountSecureId}`,error:e,code:`AdvancedLogWriteError`,category:`AdvancedSink`,context:{actionRunId:t.actionRunId}}),e}}async createS3Item(e,t,n){let{projectSecureId:r,organizationId:i}=t,a=`ttl=30d`;typeof n==`number`&&(n===1?a=`ttl=1d`:n===7&&(a=`ttl=7d`));let o=Math.floor(Date.now()/1e3)+(n??30)*24*60*60,s=process.env.ADVANCED_LOGS_BUCKET;if(!s)throw Error(`ADVANCED_LOGS_BUCKET environment variable is not set`);return{Bucket:s,Key:this.generateS3Key(t,i,r),Body:this.serialize(e,t,o,10485760),ContentType:`application/json`,Expires:new Date(o*1e3),Tagging:a}}generateS3Key(e,t,n){return`actionId`in e?`${t}/${n}/${e.actionRunId}/${e.actionRunId}.json`:`${t}/${n}/${e.actionRunId}/steps/${e.stepId}.json`}serialize(e,t,n,r){return`actionId`in t?JSON.stringify(this.serializeActionResult(e,t,n,r)):JSON.stringify(this.serializeStepResult(e,t))}serializeActionResult(n,r,i,a){let o=t({req:{...n.headers&&{headers:{...n.headers}}},res:{...r.headers&&{headers:{...r.headers}}}},e.PARTIAL),s=o.req?.headers,c=o.res?.headers,l=t(n.body,e.PARTIAL),u=t(r.body,e.PARTIAL),d=this.isBackgroundLog(n);return{data:{request:{id:r.actionRunId,actionId:r.actionId,method:r.httpMethod,headers:this.filterHeaders(s),url:{url:n.url,path:n.pathParams,queryParams:n.queryParams},body:l},response:{statusCode:r.statusCode??500,headers:this.filterHeaders(c),body:u},...d?{isBackgroundLog:!0}:{}},metadata:{...d?{isBackgroundLog:!0}:{},expirationTime:i},expirationTime:i}}serializeStepResult(e,t){return{}}filterHeaders(e){if(!e)return;let t={},n=s.map(e=>e.toLowerCase());for(let[r,i]of Object.entries(e))n.includes(r.toLowerCase())||(t[r]=i);return t}isDataSyncRequest(e){return e.mode===`data_sync`}isBackgroundLog(e){return n(e.mode)&&[`refresh_authentication`].includes(e.mode)}getContentType(e){if(!e)return;let t=[`content-type`,`contenttype`];return Object.entries(e).find(([e])=>t.includes(e.toLowerCase()))?.[1]?.toString()}};const l=(e,t)=>{try{return new i(e)??void 0}catch(e){let n=e;t?.error({message:`Error building s3 client: ${n.message}`,error:n,code:`BuildS3ClientError`,category:`buildS3Client`});return}},u=(e,t)=>{try{return new a(e)??void 0}catch(e){let n=e;t?.error({message:`Error building kafka client: ${n.message}`,error:n,code:`BuildKafkaClientError`,category:`buildKafkaClient`});return}},d=e=>{try{return JSON.stringify(e)}catch{return`[Unserializable payload]`}};var f=class{#e;#t;#n;constructor(e,t){this.#e=e,this.#n=t}async initialize(){if(!this.#e){this.#n?.warning({message:`No kafka client provided, logs sink cannot be initialized`,category:`LogsSink`});return}if(!this.#t){try{this.#t=this.#e.producer({createPartitioner:o.DefaultPartitioner})}catch(e){this.#n?.error({message:`Failed to create kafka producer for logs sink`,code:`KafkaProducerCreationError`,category:`LogsSink`,error:e}),this.#t=void 0;return}try{await this.#t.connect()}catch(e){this.#n?.error?.({message:`Failed to connect kafka producer for logs sink`,code:`KafkaProducerConnectionError`,category:`LogsSink`,error:e}),this.#t=void 0;return}this.#n?.info({message:`Logs sink kafka producer initialized`,category:`LogsSink`})}}async send(e,t,n,r){if(!r?.enabled){this.#n?.debug({message:`Logs sink is disabled, skipping sending to log sink`,category:`KafkaSink`});return}if(!this.#t)throw this.#n?.error({message:`Kafka not initialized, dropping message for topic ${e}`,category:`KafkaSink`,code:`KafkaNotReady`}),Error(`Kafka client is not initialized`);let i=d(this.buildLog(t,n));this.#n?.debug({message:`Sending to topic ${e}: ${i}`,category:`KafkaSink`});try{let t=await this.#t.send({topic:e,messages:[{value:i}]});this.#n?.debug({message:`Kafka producer response: ${JSON.stringify(t)}`,category:`KafkaSink`})}catch(t){throw this.#n?.error({message:`Error sending to topic ${e}: ${t.message}`,category:`KafkaSink`,error:t,code:`KafkaSendError`}),t}}buildLog(e,t){if(this.isActionResult(t))return Object.fromEntries(Object.entries({actionRunId:t.actionRunId,actionId:t.actionId,organizationId:t.organizationId,projectSecureId:t.projectSecureId,accountSecureId:t.accountSecureId,mode:e.mode,connectorKey:t.connectorKey,connectorVersion:t.connectorVersion,actionType:t.actionType,category:t.category,originOwnerId:t.originOwnerId,originOwnerName:t.originOwnerName,httpMethod:t.httpMethod,url:t.url,sourceId:e.sourceId,sourceType:e.sourceType,sourceValue:e.sourceValue,success:t.success,statusCode:t.statusCode,startTime:t.startTime,endTime:t.endTime,durationMs:this.calculateDuration(t.startTime,t.endTime),eventTime:new Date}).filter(([,e])=>e!==void 0));if(this.isStepResult(t))return Object.fromEntries(Object.entries({actionRunId:t.actionRunId,stepId:t.stepId,skipped:t.skipped,status:t.status,message:t.message,startTime:t.startTime,endTime:t.endTime,durationMs:this.calculateDuration(t.startTime,t.endTime),eventTime:new Date}).filter(([,e])=>e!==void 0));throw this.#n?.error({message:`Unknown result type, cannot build log`,category:`KafkaSink`,code:`UnknownResultType`}),Error(`Unknown result type`)}calculateDuration(e,t){if(!(e instanceof Date)||!(t instanceof Date))return;let n=e.getTime(),r=t.getTime();if(!(Number.isNaN(n)||Number.isNaN(r)||r<n))return r-n}isActionResult(e){return typeof e==`object`&&!!e&&`actionRunId`in e&&`actionId`in e}isStepResult(e){return typeof e==`object`&&!!e&&`actionRunId`in e&&`stepId`in e}};const p={logs:{enabled:!0},advanced:{enabled:!1,ttl:7,errorsOnly:!1}};function m(e){return{...p,...e}}var h=class{#e;#t;#n;#r;#i;constructor({getKafkaClient:e=u,getS3Client:t=l,kafkaClientConfig:n,s3ClientConfig:r,logger:i}={}){this.name=`OlapClient`,this.#e=e(n,i),this.#t=t(r,i),this.#n=i,this.#r=new f(this.#e,this.#n),this.#i=new c(this.#t,this.#n)}async initialize(){await this.#r?.initialize(),await this.#i?.initialize()}async recordAction(e,t,n){let{logs:r,advanced:i}=m(n);if(r?.enabled)try{await this.#r?.send(`actions`,e,t,r)}catch(e){this.#n?.warning({message:`[OlapClient] Error sending to kafka: ${e.message}`,category:`OlapClient`,error:e})}if(i?.enabled)try{await this.#i?.send(e,t,i)}catch(e){this.#n?.warning({message:`[OlapClient] Error sending to s3: ${e.message}`,category:`OlapClient`,error:e})}}async recordStep(e,t,n){let{logs:r,advanced:i}=m(n);if(r?.enabled)try{await this.#r?.send(`steps`,e,t,r)}catch(e){this.#n?.warning({message:`[OlapClient] Error sending to kafka: ${e.message}`,category:`OlapClient`,error:e})}if(i?.enabled)try{await this.#i?.send(e,t,i)}catch(e){this.#n?.warning({message:`[OlapClient] Error sending to s3: ${e.message}`,category:`OlapClient`,error:e})}}};const g=async({getKafkaClient:e=u,getS3Client:t=l,kafkaClientConfig:n,s3ClientConfig:r,logger:i}={})=>{let a=new h({getKafkaClient:e,getS3Client:t,kafkaClientConfig:n,s3ClientConfig:r,logger:i});return await a.initialize(),a};var _=class{static{this.olapClientPromise=null}static getInstance({getKafkaClient:e,getS3Client:t,kafkaClientConfig:n,s3ClientConfig:r,logger:i,getOlapClient:a=g}={}){return this.olapClientPromise||=a({getKafkaClient:e,getS3Client:t,kafkaClientConfig:n,s3ClientConfig:r,logger:i}),this.olapClientPromise}static resetInstance(){this.olapClientPromise=null}};export{h as OlapClient,_ as OlapClientManager};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackone/olap",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",