diff --git a/src/service-account/util/Token.ts b/src/service-account/util/Token.ts index b75918b7..8a242e49 100644 --- a/src/service-account/util/Token.ts +++ b/src/service-account/util/Token.ts @@ -7,8 +7,9 @@ import jwt from "jsonwebtoken"; import { errorMessages } from "../errors/Messages"; import { printLog } from "../../vault-api/utils/logs-helper"; import logs from "../../vault-api/utils/logs"; -import { MessageType } from "../../vault-api/utils/common"; +import { MessageType, SDK_METRICS_HEADER_KEY } from "../../vault-api/utils/common"; import SkyflowError from '../../vault-api/libs/SkyflowError'; +import { generateSDKMetrics } from "../../vault-api/utils/helpers"; export type ResponseToken = { accessToken: string, tokenType: string } export type ResponseSignedDataTokens = { token: string, signedToken: string } @@ -119,7 +120,10 @@ function getToken(credentials, options?: BearerTokenOptions): Promise; @@ -52,6 +53,7 @@ class Client { request = (request: IClientRequest) => new Promise((resolve, reject) => { const headerKeys = toLowerKeys(request.headers); let contentType = headerKeys['content-type'] + headerKeys[SDK_METRICS_HEADER_KEY] = JSON.stringify(generateSDKMetrics()); const data = this.convertRequestBody(request.body,contentType) const headers = this.getHeaders(data,headerKeys) axios({ diff --git a/src/vault-api/utils/common/index.ts b/src/vault-api/utils/common/index.ts index ef7d7c4c..bed77a23 100644 --- a/src/vault-api/utils/common/index.ts +++ b/src/vault-api/utils/common/index.ts @@ -126,4 +126,6 @@ export interface IUpdateInput{ export interface IUpdateOptions{ tokens: boolean -} \ No newline at end of file +} + +export const SDK_METRICS_HEADER_KEY = "sky-metadata"; \ No newline at end of file diff --git a/src/vault-api/utils/helpers/index.ts b/src/vault-api/utils/helpers/index.ts index b70af524..5c8219ae 100644 --- a/src/vault-api/utils/helpers/index.ts +++ b/src/vault-api/utils/helpers/index.ts @@ -1,11 +1,14 @@ /* Copyright (c) 2022 Skyflow, Inc. */ -import axios from "axios"; -import Client, { IClientRequest } from "../../client"; -import { ContentType, IConnectionConfig } from "../common"; -const qs = require('qs'); -const FormData = require('form-data'); +import * as sdkDetails from "../../../../package.json"; +import { MessageType } from "../common"; +import logs from "../logs"; +import { parameterizedString, printLog } from "../logs-helper"; +const FormData = require("form-data"); +const os = require("os"); +const process = require("process"); + export function fillUrlWithPathAndQueryParams(url:string, pathParams?:object, queryParams?:object) { @@ -60,3 +63,61 @@ export function objectToFormData(obj: any, form?: FormData, namespace?: string) return fd; } + + +export const generateSDKMetrics = () => { + let sdkNameVersion = ""; + let clientDeviceModel = ""; + let clientOSDetails = ""; + let runtimeDetails = ""; + try { + sdkNameVersion = `${sdkDetails.name ? `${sdkDetails.name}@` : ""}${ + sdkDetails.version ? sdkDetails.version : "" + }`; + } catch (err) { + printLog( + parameterizedString(logs.infoLogs.UNABLE_TO_GENERATE_SDK_METRIC,"sdkNameVersion") + ,MessageType.LOG + ); + sdkNameVersion = ""; + } + + try { + clientDeviceModel = `${process.platform ? `${process.platform} ` : ""} ${ + process.arch ? process.arch : "" + }`; + } catch (err) { + printLog( + parameterizedString(logs.infoLogs.UNABLE_TO_GENERATE_SDK_METRIC,"clientDeviceModel") + ,MessageType.LOG + ); + clientDeviceModel = ""; + } + + try { + clientOSDetails = `${os.version() ? os.version() : ""}`; + } catch (err) { + printLog( + parameterizedString(logs.infoLogs.UNABLE_TO_GENERATE_SDK_METRIC,"clientOSDetails") + ,MessageType.LOG + ); + clientOSDetails = ""; + } + + try { + runtimeDetails = `${process.version ? `Node@${process.version}` : ""}`; + } catch (err) { + printLog( + parameterizedString(logs.infoLogs.UNABLE_TO_GENERATE_SDK_METRIC,"runtimeDetails") + ,MessageType.LOG + ); + runtimeDetails = ""; + } + + return { + sdk_name_version: sdkNameVersion, + sdk_client_device_model: clientDeviceModel, + sdk_client_os_details: clientOSDetails, + sdk_runtime_details: runtimeDetails, + }; +}; \ No newline at end of file diff --git a/src/vault-api/utils/logs.ts b/src/vault-api/utils/logs.ts index c0f7caa0..67e0043a 100644 --- a/src/vault-api/utils/logs.ts +++ b/src/vault-api/utils/logs.ts @@ -35,6 +35,7 @@ const logs = { GENERATE_SIGNED_DATA_TOKENS_TRIGGERED: "generateSignedDataTokens is triggered", UPDATE_TRIGGERED: 'Update method triggered.', UPDATE_REQUEST_RESOLVED:'Update request is resolved.', + UNABLE_TO_GENERATE_SDK_METRIC:'Unable to generate %s1 metric.' }, errorLogs: { diff --git a/test/vault-api/Client.test.js b/test/vault-api/Client.test.js index e1d32e2e..29db566c 100644 --- a/test/vault-api/Client.test.js +++ b/test/vault-api/Client.test.js @@ -3,6 +3,7 @@ */ import Client from "../../src/vault-api/client"; import axios from "axios"; +import { generateSDKMetrics } from "../../src/vault-api/utils/helpers"; jest.mock("axios", () => { return { @@ -30,7 +31,7 @@ describe("Client Class",()=>{ headers: { "Content-Type": "application/json" }, }; const data = JSON.stringify({ name: "John Doe", age: 30 }); - const headers = { "content-type": "application/json" }; + const headers = { "content-type": "application/json","sky-metadata":JSON.stringify(generateSDKMetrics()) }; axios.mockImplementation(() => Promise.resolve({ data: { message: "Success" } }) ); diff --git a/test/vault-api/Util.test.js b/test/vault-api/Util.test.js index a8145434..a3a35035 100644 --- a/test/vault-api/Util.test.js +++ b/test/vault-api/Util.test.js @@ -8,9 +8,16 @@ import { import SKYFLOW_ERROR_CODE from "../../src/vault-api/utils/constants"; import { parameterizedString } from "../../src/vault-api/utils/logs-helper"; import SkyflowError from "../../src/vault-api/libs/SkyflowError"; -import { fillUrlWithPathAndQueryParams, formatVaultURL, toLowerKeys, objectToFormData } from "../../src/vault-api/utils/helpers"; +import { fillUrlWithPathAndQueryParams, formatVaultURL, toLowerKeys, objectToFormData, generateSDKMetrics } from "../../src/vault-api/utils/helpers"; const FormData = require('form-data'); +let mockJson = {}; +jest.mock('../../package.json',()=>(mockJson)); +let mockProcess = {}; +jest.mock('process',()=>(mockProcess)); +let mockOS= {}; +jest.mock('os',()=>(mockOS)) + describe("validate upsert options in collect", () => { it("invalid upsert options type", () => { try { @@ -553,4 +560,37 @@ describe("URL helper its", () => { const result = objectToFormData(obj); expect(result).toBeDefined(); }); -}); \ No newline at end of file +}); + +describe("test generateSDKMetrics",()=>{ + + test('should set it empty string when name version are undefined',()=>{ + mockJson = {name:undefined,version:null} + const metrics = generateSDKMetrics(); + expect(metrics.sdk_name_version).toBe(''); + }); + + test('should set it device model empty string when process is invalid or empty',()=>{ + mockProcess = {}; + const metrics = generateSDKMetrics(); + expect(metrics.sdk_client_device_model).toBe(''); + }); + + test('should set it run time details empty string when process is invalid or empty',()=>{ + mockProcess = {}; + const metrics = generateSDKMetrics(); + expect(metrics.sdk_runtime_details).toBe(''); + }); + + test('should set it os details empty string when process is invalid or empty',()=>{ + mockOS = {}; + const metrics = generateSDKMetrics(); + expect(metrics.sdk_client_os_details).toBe(''); + }); + + + + + + +}); \ No newline at end of file