Move from openapi-generator-cli to custom Axios approach, and add listFolders endpoint

This commit is contained in:
carlosmonastyrski
2025-05-02 19:12:08 -03:00
parent 1b8aa6d20e
commit 68613f6b13
25 changed files with 1757 additions and 2605 deletions

View File

@@ -1,6 +1,6 @@
import { InfisicalSDK } from "..";
import { ApiV1AuthUniversalAuthLoginPostRequest } from "../infisicalapi_client";
import { DefaultApi as InfisicalApi } from "../infisicalapi_client";
import { AuthApi } from "../api/endpoints/auth";
import { UniversalAuthLoginRequest } from "../api/types";
import { MACHINE_IDENTITY_ID_ENV_NAME } from "./constants";
import { InfisicalSDKError, newInfisicalError } from "./errors";
import { getAwsRegion, performAwsIamLogin } from "./util";
@@ -8,97 +8,103 @@ import { getAwsRegion, performAwsIamLogin } from "./util";
type AuthenticatorFunction = (accessToken: string) => InfisicalSDK;
type AwsAuthLoginOptions = {
identityId?: string;
identityId?: string;
};
export const renewToken = async (apiClient: InfisicalApi, token?: string) => {
try {
if (!token) {
throw new InfisicalSDKError("Unable to renew access token, no access token set. Are you sure you're authenticated?");
}
export const renewToken = async (apiClient: AuthApi, token?: string) => {
try {
if (!token) {
throw new InfisicalSDKError(
"Unable to renew access token, no access token set."
);
}
const res = await apiClient.apiV1AuthTokenRenewPost({
apiV1AuthTokenRenewPostRequest: {
accessToken: token
}
});
return res.data;
} catch (err) {
throw newInfisicalError(err);
}
const res = await apiClient.renewToken({ accessToken: token });
return res;
} catch (err) {
throw newInfisicalError(err);
}
};
export default class AuthClient {
#sdkAuthenticator: AuthenticatorFunction;
#apiClient: InfisicalApi;
#accessToken?: string;
#sdkAuthenticator: AuthenticatorFunction;
#apiClient: AuthApi;
#accessToken?: string;
constructor(authenticator: AuthenticatorFunction, apiInstance: InfisicalApi, accessToken?: string) {
this.#sdkAuthenticator = authenticator;
this.#apiClient = apiInstance;
this.#accessToken = accessToken;
}
constructor(
authenticator: AuthenticatorFunction,
apiInstance: AuthApi,
accessToken?: string
) {
this.#sdkAuthenticator = authenticator;
this.#apiClient = apiInstance;
this.#accessToken = accessToken;
}
awsIamAuth = {
login: async (options?: AwsAuthLoginOptions) => {
try {
const identityId = options?.identityId || process.env[MACHINE_IDENTITY_ID_ENV_NAME];
awsIamAuth = {
login: async (options?: AwsAuthLoginOptions) => {
try {
const identityId =
options?.identityId || process.env[MACHINE_IDENTITY_ID_ENV_NAME];
if (!identityId) {
throw new InfisicalSDKError(
"Identity ID is required for AWS IAM authentication"
);
}
if (!identityId) {
throw new InfisicalSDKError("Identity ID is required for AWS IAM authentication");
}
const iamRequest = await performAwsIamLogin(await getAwsRegion());
const res = await this.#apiClient.awsIamAuthLogin({
iamHttpRequestMethod: iamRequest.iamHttpRequestMethod,
iamRequestBody: Buffer.from(iamRequest.iamRequestBody).toString(
"base64"
),
iamRequestHeaders: Buffer.from(
JSON.stringify(iamRequest.iamRequestHeaders)
).toString("base64"),
identityId,
});
const iamRequest = await performAwsIamLogin(await getAwsRegion());
return this.#sdkAuthenticator(res.accessToken);
} catch (err) {
throw newInfisicalError(err);
}
},
renew: async () => {
try {
const refreshedToken = await renewToken(
this.#apiClient,
this.#accessToken
);
return this.#sdkAuthenticator(refreshedToken.accessToken);
} catch (err) {
throw newInfisicalError(err);
}
},
};
const res = await this.#apiClient.apiV1AuthAwsAuthLoginPost({
apiV1AuthAwsAuthLoginPostRequest: {
iamHttpRequestMethod: iamRequest.iamHttpRequestMethod,
iamRequestBody: Buffer.from(iamRequest.iamRequestBody).toString("base64"),
iamRequestHeaders: Buffer.from(JSON.stringify(iamRequest.iamRequestHeaders)).toString("base64"),
identityId
}
});
universalAuth = {
login: async (options: UniversalAuthLoginRequest) => {
try {
const res = await this.#apiClient.universalAuthLogin(options);
return this.#sdkAuthenticator(res.accessToken);
} catch (err) {
throw newInfisicalError(err);
}
},
renew: async () => {
try {
const refreshedToken = await renewToken(
this.#apiClient,
this.#accessToken
);
return this.#sdkAuthenticator(refreshedToken.accessToken);
} catch (err) {
throw newInfisicalError(err);
}
},
};
return this.#sdkAuthenticator(res.data.accessToken);
} catch (err) {
throw newInfisicalError(err);
}
},
renew: async () => {
try {
const refreshedToken = await renewToken(this.#apiClient, this.#accessToken);
return this.#sdkAuthenticator(refreshedToken.accessToken);
} catch (err) {
throw newInfisicalError(err);
}
}
};
universalAuth = {
login: async (options: ApiV1AuthUniversalAuthLoginPostRequest) => {
try {
const res = await this.#apiClient.apiV1AuthUniversalAuthLoginPost({
apiV1AuthUniversalAuthLoginPostRequest: options
});
return this.#sdkAuthenticator(res.data.accessToken);
} catch (err) {
throw newInfisicalError(err);
}
},
renew: async () => {
try {
const refreshedToken = await renewToken(this.#apiClient, this.#accessToken);
return this.#sdkAuthenticator(refreshedToken.accessToken);
} catch (err) {
throw newInfisicalError(err);
}
}
};
accessToken = (token: string) => {
return this.#sdkAuthenticator(token);
};
accessToken = (token: string) => {
return this.#sdkAuthenticator(token);
};
}

View File

@@ -1,119 +1,97 @@
import { RawAxiosRequestConfig } from "axios";
import { Configuration, DefaultApi as InfisicalApi } from "../infisicalapi_client";
import type {
ApiV1DynamicSecretsGet200ResponseDynamicSecretsInner,
ApiV1DynamicSecretsLeasesLeaseIdDelete200Response,
ApiV1DynamicSecretsLeasesPost200Response,
DefaultApiApiV1DynamicSecretsLeasesLeaseIdDeleteRequest,
DefaultApiApiV1DynamicSecretsLeasesLeaseIdRenewPostRequest,
DefaultApiApiV1DynamicSecretsLeasesPostRequest,
DefaultApiApiV1DynamicSecretsNameDeleteRequest,
DefaultApiApiV1DynamicSecretsPostRequest
} from "../infisicalapi_client";
import type { TDynamicSecretProvider } from "./schemas/dynamic-secrets";
import { DynamicSecretsApi } from "../api/endpoints/dynamic-secrets";
import { TDynamicSecretProvider } from "./schemas/dynamic-secrets";
import { newInfisicalError } from "./errors";
export type CreateDynamicSecretOptions = Omit<DefaultApiApiV1DynamicSecretsPostRequest["apiV1DynamicSecretsPostRequest"], "provider"> & {
provider: TDynamicSecretProvider;
export type CreateDynamicSecretOptions = {
provider: TDynamicSecretProvider;
defaultTTL: string;
maxTTL: string;
name: string;
projectSlug: string;
environmentSlug: string;
path?: string;
metadata?: Record<string, any>;
};
export type DeleteDynamicSecretOptions = DefaultApiApiV1DynamicSecretsNameDeleteRequest["apiV1DynamicSecretsNameDeleteRequest"];
export type CreateDynamicSecretLeaseOptions = DefaultApiApiV1DynamicSecretsLeasesPostRequest["apiV1DynamicSecretsLeasesPostRequest"];
export type DeleteDynamicSecretLeaseOptions =
DefaultApiApiV1DynamicSecretsLeasesLeaseIdDeleteRequest["apiV1DynamicSecretsLeasesLeaseIdDeleteRequest"];
export type RenewDynamicSecretLeaseOptions =
DefaultApiApiV1DynamicSecretsLeasesLeaseIdRenewPostRequest["apiV1DynamicSecretsLeasesLeaseIdRenewPostRequest"];
export type CreateDynamicSecretResult = ApiV1DynamicSecretsGet200ResponseDynamicSecretsInner;
export type DeleteDynamicSecretResult = ApiV1DynamicSecretsGet200ResponseDynamicSecretsInner;
export type CreateDynamicSecretLeaseResult = ApiV1DynamicSecretsLeasesPost200Response;
export type DeleteDynamicSecretLeaseResult = ApiV1DynamicSecretsLeasesLeaseIdDelete200Response;
export type RenewDynamicSecretLeaseResult = ApiV1DynamicSecretsLeasesLeaseIdDelete200Response;
export type DeleteDynamicSecretOptions = {
environmentSlug: string;
projectSlug: string;
path?: string;
isForced?: boolean;
};
export type CreateDynamicSecretLeaseOptions = {
dynamicSecretName: string;
environmentSlug: string;
projectSlug: string;
path?: string;
ttl?: string;
};
export type DeleteDynamicSecretLeaseOptions = {
environmentSlug: string;
projectSlug: string;
path?: string;
isForced?: boolean;
};
export type RenewDynamicSecretLeaseOptions = {
environmentSlug: string;
projectSlug: string;
path?: string;
ttl?: string;
};
export default class DynamicSecretsClient {
#apiInstance: InfisicalApi;
#requestOptions: RawAxiosRequestConfig | undefined;
constructor(apiInstance: InfisicalApi, requestOptions: RawAxiosRequestConfig | undefined) {
this.#apiInstance = apiInstance;
this.#requestOptions = requestOptions;
}
constructor(private apiClient: DynamicSecretsApi) {}
async create(options: CreateDynamicSecretOptions): Promise<CreateDynamicSecretResult> {
try {
const res = await this.#apiInstance.apiV1DynamicSecretsPost(
{
apiV1DynamicSecretsPostRequest: options as DefaultApiApiV1DynamicSecretsPostRequest["apiV1DynamicSecretsPostRequest"]
},
this.#requestOptions
);
async create(options: CreateDynamicSecretOptions) {
try {
const res = await this.apiClient.create(options);
return res.dynamicSecret;
} catch (err) {
throw newInfisicalError(err);
}
}
return res.data.dynamicSecret;
} catch (err) {
throw newInfisicalError(err);
}
}
async delete(dynamicSecretName: string, options: DeleteDynamicSecretOptions) {
try {
const res = await this.apiClient.delete(dynamicSecretName, options);
return res.dynamicSecret;
} catch (err) {
throw newInfisicalError(err);
}
}
async delete(dynamicSecretName: string, options: DeleteDynamicSecretOptions): Promise<DeleteDynamicSecretResult> {
try {
const res = await this.#apiInstance.apiV1DynamicSecretsNameDelete(
{
name: dynamicSecretName,
apiV1DynamicSecretsNameDeleteRequest: options
},
this.#requestOptions
);
leases = {
create: async (options: CreateDynamicSecretLeaseOptions) => {
try {
const res = await this.apiClient.leases.create(options);
return res;
} catch (err) {
throw newInfisicalError(err);
}
},
return res.data.dynamicSecret;
} catch (err) {
throw newInfisicalError(err);
}
}
delete: async (
leaseId: string,
options: DeleteDynamicSecretLeaseOptions
) => {
try {
const res = await this.apiClient.leases.delete(leaseId, options);
return res;
} catch (err) {
throw newInfisicalError(err);
}
},
leases = {
create: async (options: CreateDynamicSecretLeaseOptions): Promise<CreateDynamicSecretLeaseResult> => {
try {
const res = await this.#apiInstance.apiV1DynamicSecretsLeasesPost(
{
apiV1DynamicSecretsLeasesPostRequest: options
},
this.#requestOptions
);
return res.data;
} catch (err) {
throw newInfisicalError(err);
}
},
delete: async (leaseId: string, options: DeleteDynamicSecretLeaseOptions): Promise<DeleteDynamicSecretLeaseResult> => {
try {
const res = await this.#apiInstance.apiV1DynamicSecretsLeasesLeaseIdDelete(
{
leaseId: leaseId,
apiV1DynamicSecretsLeasesLeaseIdDeleteRequest: options
},
this.#requestOptions
);
return res.data;
} catch (err) {
throw newInfisicalError(err);
}
},
renew: async (leaseId: string, options: RenewDynamicSecretLeaseOptions): Promise<RenewDynamicSecretLeaseResult> => {
try {
const res = await this.#apiInstance.apiV1DynamicSecretsLeasesLeaseIdRenewPost(
{
leaseId: leaseId,
apiV1DynamicSecretsLeasesLeaseIdRenewPostRequest: options
},
this.#requestOptions
);
return res.data;
} catch (err) {
throw newInfisicalError(err);
}
}
};
renew: async (leaseId: string, options: RenewDynamicSecretLeaseOptions) => {
try {
const res = await this.apiClient.leases.renew(leaseId, options);
return res;
} catch (err) {
throw newInfisicalError(err);
}
},
};
}

View File

@@ -1,33 +1,22 @@
import { RawAxiosRequestConfig } from "axios";
import { DefaultApi as InfisicalApi } from "../infisicalapi_client";
import type { ApiV1WorkspaceWorkspaceIdEnvironmentsPostRequest, ApiV1WorkspaceWorkspaceIdEnvironmentsPost200Response } from "../infisicalapi_client";
import { EnvironmentsApi } from "../api/endpoints/environments";
import { newInfisicalError } from "./errors";
export type CreateEnvironmentOptions = {
projectId: string;
} & ApiV1WorkspaceWorkspaceIdEnvironmentsPostRequest;
export type CreateEnvironmentResult = ApiV1WorkspaceWorkspaceIdEnvironmentsPost200Response;
name: string;
projectId: string;
slug: string;
position?: number;
};
export default class EnvironmentsClient {
#apiInstance: InfisicalApi;
#requestOptions: RawAxiosRequestConfig | undefined;
constructor(apiInstance: InfisicalApi, requestOptions: RawAxiosRequestConfig | undefined) {
this.#apiInstance = apiInstance;
this.#requestOptions = requestOptions;
}
constructor(private apiClient: EnvironmentsApi) {}
create = async (options: CreateEnvironmentOptions): Promise<CreateEnvironmentResult["environment"]> => {
try {
const res = await this.#apiInstance.apiV1WorkspaceWorkspaceIdEnvironmentsPost(
{
workspaceId: options.projectId,
apiV1WorkspaceWorkspaceIdEnvironmentsPostRequest: options
},
this.#requestOptions
);
return res.data.environment;
} catch (err) {
throw newInfisicalError(err);
}
};
create = async (options: CreateEnvironmentOptions) => {
try {
const res = await this.apiClient.create(options);
return res.environment;
} catch (err) {
throw newInfisicalError(err);
}
};
}

View File

@@ -1,57 +1,49 @@
import { AxiosError } from "axios";
type TApiErrorResponse = {
statusCode: number;
message: string;
error: string;
};
export class InfisicalSDKError extends Error {
constructor(message: string) {
super(message);
this.message = message;
this.name = "InfisicalSDKError";
}
constructor(message: string) {
super(message);
this.message = message;
this.name = "InfisicalSDKError";
}
}
export class InfisicalSDKRequestError extends Error {
constructor(
message: string,
requestData: {
url: string;
method: string;
statusCode: number;
}
) {
super(message);
this.message = `[URL=${requestData.url}] [Method=${requestData.method}] [StatusCode=${requestData.statusCode}] ${message}`;
this.name = "InfisicalSDKRequestError";
}
constructor(
message: string,
requestData: {
url: string;
method: string;
statusCode: number;
}
) {
super(message);
this.message = `[URL=${requestData.url}] [Method=${requestData.method}] [StatusCode=${requestData.statusCode}] ${message}`;
this.name = "InfisicalSDKRequestError";
}
}
export const newInfisicalError = (error: any) => {
if (error instanceof AxiosError) {
const data = error?.response?.data as TApiErrorResponse;
if (error?.isAxiosError) {
const data = error?.response?.data;
if (data?.message) {
let message = data.message;
if (error.status === 422) {
message = JSON.stringify(data);
}
if (data?.message) {
let message = data.message;
if (error.response?.status === 422) {
message = JSON.stringify(data);
}
return new InfisicalSDKRequestError(message, {
url: error.response?.config.url || "",
method: error.response?.config.method || "",
statusCode: error.response?.status || 0
});
} else if (error.message) {
return new InfisicalSDKError(error.message);
} else if (error.code) {
// If theres no message but a code is present, it's likely to be an aggregation error. This is not specific to Axios, but it falls under the AxiosError type
return new InfisicalSDKError(error.code);
} else {
return new InfisicalSDKError("Request failed with unknown error");
}
}
return new InfisicalSDKRequestError(message, {
url: error.response?.config.url || "",
method: error.response?.config.method || "",
statusCode: error.response?.status || 0,
});
} else if (error.message) {
return new InfisicalSDKError(error.message);
} else if (error.code) {
return new InfisicalSDKError(error.code);
} else {
return new InfisicalSDKError("Request failed with unknown error");
}
}
return new InfisicalSDKError(error?.message || "An error occurred");
return new InfisicalSDKError(error?.message || "An error occurred");
};

View File

@@ -1,35 +1,52 @@
import { RawAxiosRequestConfig } from "axios";
import { DefaultApi as InfisicalApi } from "../infisicalapi_client";
import type { ApiV1FoldersPostRequest, ApiV1FoldersPost200Response } from "../infisicalapi_client";
import { FoldersApi } from "../api/endpoints/folders";
import { newInfisicalError } from "./errors";
export type CreateFolderOptions = {
projectId: string;
} & Omit<ApiV1FoldersPostRequest, "workspaceId" | "directory">;
export type CreateFolderResult = ApiV1FoldersPost200Response;
name: string;
path: string;
projectId: string;
environment: string;
description?: string;
};
export type ListFoldersOptions = {
environment: string;
projectId: string;
path?: string;
recursive?: boolean;
lastSecretModified?: string;
};
export default class FoldersClient {
#apiInstance: InfisicalApi;
#requestOptions: RawAxiosRequestConfig | undefined;
constructor(apiInstance: InfisicalApi, requestOptions: RawAxiosRequestConfig | undefined) {
this.#apiInstance = apiInstance;
this.#requestOptions = requestOptions;
}
constructor(private apiClient: FoldersApi) {}
create = async (options: CreateFolderOptions): Promise<CreateFolderResult["folder"]> => {
try {
const res = await this.#apiInstance.apiV1FoldersPost(
{
apiV1FoldersPostRequest: {
...options,
workspaceId: options.projectId
}
},
this.#requestOptions
);
return res.data.folder;
} catch (err) {
throw newInfisicalError(err);
}
};
create = async (options: CreateFolderOptions) => {
try {
const res = await this.apiClient.create({
name: options.name,
path: options.path,
workspaceId: options.projectId,
environment: options.environment,
description: options.description,
});
return res.folder;
} catch (err) {
throw newInfisicalError(err);
}
};
listFolders = async (options: ListFoldersOptions) => {
try {
const res = await this.apiClient.listFolders({
environment: options.environment,
workspaceId: options.projectId,
path: options.path,
recursive: options.recursive,
lastSecretModified: options.lastSecretModified,
});
return res.folders;
} catch (err) {
throw newInfisicalError(err);
}
};
}

View File

@@ -1,56 +1,44 @@
import { RawAxiosRequestConfig } from "axios";
import { DefaultApi as InfisicalApi } from "../infisicalapi_client";
import type {
ApiV2WorkspacePost200Response,
ApiV2WorkspacePostRequest,
ApiV2WorkspaceProjectIdMembershipsPost200Response,
ApiV2WorkspaceProjectIdMembershipsPostRequest
} from "../infisicalapi_client";
import { ProjectsApi } from "../api/endpoints/projects";
import { newInfisicalError } from "./errors";
export type CreateProjectOptions = ApiV2WorkspacePostRequest;
export type CreateProjectResult = ApiV2WorkspacePost200Response;
export type CreateProjectOptions = {
projectName: string;
type: string;
projectDescription?: string;
slug?: string;
template?: string;
kmsKeyId?: string;
};
export type InviteMemberToProjectOptions = {
projectId: string;
emails?: string[];
usernames?: string[];
roleSlugs?: string[];
};
export type InviteMemberToProjectOptions = { projectId: string } & ApiV2WorkspaceProjectIdMembershipsPostRequest;
export type InviteMemberToProjectResult = ApiV2WorkspaceProjectIdMembershipsPost200Response;
export default class ProjectsClient {
#apiInstance: InfisicalApi;
#requestOptions: RawAxiosRequestConfig | undefined;
constructor(apiInstance: InfisicalApi, requestOptions: RawAxiosRequestConfig | undefined) {
this.#apiInstance = apiInstance;
this.#requestOptions = requestOptions;
}
constructor(private apiClient: ProjectsApi) {}
create = async (options: CreateProjectOptions): Promise<CreateProjectResult["project"]> => {
try {
const res = await this.#apiInstance.apiV2WorkspacePost(
{
apiV2WorkspacePostRequest: options
},
this.#requestOptions
);
return res.data.project;
} catch (err) {
throw newInfisicalError(err);
}
};
create = async (options: CreateProjectOptions) => {
try {
const res = await this.apiClient.create(options);
return res.project;
} catch (err) {
throw newInfisicalError(err);
}
};
inviteMembers = async (options: InviteMemberToProjectOptions): Promise<InviteMemberToProjectResult["memberships"]> => {
try {
if (!options.usernames?.length && !options.emails?.length) {
throw new Error("Either usernames or emails must be provided");
}
inviteMembers = async (options: InviteMemberToProjectOptions) => {
try {
if (!options.usernames?.length && !options.emails?.length) {
throw new Error("Either usernames or emails must be provided");
}
const res = await this.#apiInstance.apiV2WorkspaceProjectIdMembershipsPost(
{
projectId: options.projectId,
apiV2WorkspaceProjectIdMembershipsPostRequest: options
},
this.#requestOptions
);
return res.data.memberships;
} catch (err) {
throw newInfisicalError(err);
}
};
const res = await this.apiClient.inviteMembers(options);
return res.memberships;
} catch (err) {
throw newInfisicalError(err);
}
};
}

View File

@@ -1,215 +1,192 @@
import { RawAxiosRequestConfig } from "axios";
import { DefaultApi as InfisicalApi } from "../infisicalapi_client";
import type {
ApiV3SecretsRawGet200Response,
ApiV3SecretsRawSecretNameGet200Response,
ApiV3SecretsRawSecretNamePost200Response,
DefaultApiApiV3SecretsRawSecretNameDeleteRequest,
DefaultApiApiV3SecretsRawSecretNamePatchRequest,
DefaultApiApiV3SecretsRawSecretNamePostRequest
} from "../infisicalapi_client";
import { SecretsApi } from "../api/endpoints/secrets";
import { Secret } from "../api/types";
import { newInfisicalError } from "./errors";
import { getUniqueSecretsByKey } from "./util";
type SecretType = "shared" | "personal";
type ListSecretsOptions = {
environment: string;
projectId: string;
expandSecretReferences?: boolean;
includeImports?: boolean;
recursive?: boolean;
secretPath?: string;
tagSlugs?: string[];
viewSecretValue?: boolean;
environment: string;
projectId: string;
expandSecretReferences?: boolean;
includeImports?: boolean;
recursive?: boolean;
secretPath?: string;
tagSlugs?: string[];
viewSecretValue?: boolean;
};
type GetSecretOptions = {
environment: string;
secretName: string;
expandSecretReferences?: boolean;
includeImports?: boolean;
secretPath?: string;
type?: SecretType;
version?: number;
projectId: string;
viewSecretValue?: boolean;
};
type BaseSecretOptions = {
environment: string;
secretName: string;
expandSecretReferences?: boolean;
includeImports?: boolean;
projectId: string;
secretComment?: string;
secretPath?: string;
secretReminderNote?: string;
secretReminderRepeatDays?: number;
skipMultilineEncoding?: boolean;
tagIds?: string[];
type?: SecretType;
version?: number;
projectId: string;
viewSecretValue?: boolean;
metadata?: Record<string, any>;
secretMetadata?: Record<string, any>[];
};
export type UpdateSecretOptions = Omit<DefaultApiApiV3SecretsRawSecretNamePatchRequest["apiV3SecretsRawSecretNamePatchRequest"], "workspaceId"> & {
projectId: string;
};
export type UpdateSecretOptions = {
secretValue?: string;
newSecretName?: string;
} & BaseSecretOptions;
export type CreateSecretOptions = Omit<DefaultApiApiV3SecretsRawSecretNamePostRequest["apiV3SecretsRawSecretNamePostRequest"], "workspaceId"> & {
projectId: string;
};
export type CreateSecretOptions = {
secretValue: string;
} & BaseSecretOptions;
export type DeleteSecretOptions = Omit<DefaultApiApiV3SecretsRawSecretNameDeleteRequest["apiV3SecretsRawSecretNameDeleteRequest"], "workspaceId"> & {
projectId: string;
export type DeleteSecretOptions = {
environment: string;
projectId: string;
secretPath?: string;
type?: SecretType;
};
export type ListSecretsResult = ApiV3SecretsRawGet200Response;
export type GetSecretResult = ApiV3SecretsRawSecretNameGet200Response["secret"];
export type UpdateSecretResult = ApiV3SecretsRawSecretNamePost200Response;
export type CreateSecretResult = ApiV3SecretsRawSecretNamePost200Response;
export type DeleteSecretResult = ApiV3SecretsRawSecretNamePost200Response;
const convertBool = (value?: boolean) => (value ? "true" : "false");
const defaultBoolean = (value?: boolean, defaultValue: boolean = false) => {
if (value === undefined) {
return defaultValue;
}
return value;
if (value === undefined) {
return defaultValue;
}
return value;
};
export default class SecretsClient {
#apiInstance: InfisicalApi;
#requestOptions: RawAxiosRequestConfig | undefined;
constructor(apiInstance: InfisicalApi, requestOptions: RawAxiosRequestConfig | undefined) {
this.#apiInstance = apiInstance;
this.#requestOptions = requestOptions;
}
constructor(private apiClient: SecretsApi) {}
listSecrets = async (options: ListSecretsOptions): Promise<ListSecretsResult> => {
try {
const res = await this.#apiInstance.apiV3SecretsRawGet(
{
viewSecretValue: convertBool(options.viewSecretValue ?? true),
environment: options.environment,
workspaceId: options.projectId,
expandSecretReferences: convertBool(defaultBoolean(options.expandSecretReferences, true)),
includeImports: convertBool(options.includeImports),
recursive: convertBool(options.recursive),
secretPath: options.secretPath,
tagSlugs: options.tagSlugs ? options.tagSlugs.join(",") : undefined
},
this.#requestOptions
);
return res.data;
} catch (err) {
throw newInfisicalError(err);
}
};
listSecrets = async (options: ListSecretsOptions) => {
try {
return await this.apiClient.listSecrets({
workspaceId: options.projectId,
environment: options.environment,
expandSecretReferences: convertBool(
defaultBoolean(options.expandSecretReferences, true)
),
includeImports: convertBool(options.includeImports),
recursive: convertBool(options.recursive),
secretPath: options.secretPath,
tagSlugs: options.tagSlugs ? options.tagSlugs.join(",") : undefined,
viewSecretValue: convertBool(options.viewSecretValue ?? true),
});
} catch (err) {
throw newInfisicalError(err);
}
};
listSecretsWithImports = async (options: Omit<ListSecretsOptions, "includeImports">): Promise<ListSecretsResult["secrets"]> => {
const res = await this.listSecrets({
...options,
includeImports: true
});
listSecretsWithImports = async (
options: Omit<ListSecretsOptions, "includeImports">
) => {
const res = await this.listSecrets({
...options,
includeImports: true,
});
let { imports, secrets } = res;
if (imports) {
if (options.recursive) {
secrets = getUniqueSecretsByKey(secrets);
}
let { imports, secrets } = res;
if (imports) {
for (const imp of imports) {
for (const importedSecret of imp.secrets) {
if (!secrets.find((s) => s.secretKey === importedSecret.secretKey)) {
secrets.push({
...importedSecret,
secretPath: imp.secretPath,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
tags: [],
});
}
}
}
}
for (const imp of imports) {
for (const importedSecret of imp.secrets) {
// CASE: We need to ensure that the imported values don't override the "base" secrets.
// Priority order is:
// Local/Preset variables -> Actual secrets -> Imported secrets (high->low)
return secrets;
};
// Check if the secret already exists in the secrets list
if (!secrets.find(s => s.secretKey === importedSecret.secretKey)) {
secrets.push({
...importedSecret,
secretPath: imp.secretPath,
// These fields are not returned by the API
updatedAt: new Date().toISOString(),
createdAt: new Date().toISOString(),
tags: []
});
}
}
}
}
getSecret = async (options: GetSecretOptions) => {
try {
const res = await this.apiClient.getSecret({
secretName: options.secretName,
workspaceId: options.projectId,
environment: options.environment,
expandSecretReferences: convertBool(
defaultBoolean(options.expandSecretReferences, true)
),
includeImports: convertBool(options.includeImports),
secretPath: options.secretPath,
type: options.type,
version: options.version,
viewSecretValue: convertBool(options.viewSecretValue ?? true),
});
return res.secret;
} catch (err) {
throw newInfisicalError(err);
}
};
return secrets;
};
updateSecret = async (secretName: string, options: UpdateSecretOptions) => {
try {
return await this.apiClient.updateSecret(secretName, {
workspaceId: options.projectId,
environment: options.environment,
secretValue: options.secretValue,
newSecretName: options.newSecretName,
secretComment: options.secretComment,
secretPath: options.secretPath,
secretReminderNote: options.secretReminderNote,
secretReminderRepeatDays: options.secretReminderRepeatDays,
skipMultilineEncoding: options.skipMultilineEncoding,
tagIds: options.tagIds,
type: options.type,
metadata: options.metadata,
});
} catch (err) {
throw newInfisicalError(err);
}
};
getSecret = async (options: GetSecretOptions): Promise<GetSecretResult> => {
try {
const res = await this.#apiInstance.apiV3SecretsRawSecretNameGet(
{
viewSecretValue: convertBool(options.viewSecretValue ?? true),
environment: options.environment,
secretName: options.secretName,
workspaceId: options.projectId,
expandSecretReferences: convertBool(defaultBoolean(options.expandSecretReferences, true)),
includeImports: convertBool(options.includeImports),
secretPath: options.secretPath,
type: options.type,
version: options.version
},
this.#requestOptions
);
return res.data.secret;
} catch (err) {
throw newInfisicalError(err);
}
};
createSecret = async (secretName: string, options: CreateSecretOptions) => {
try {
return await this.apiClient.createSecret(secretName, {
workspaceId: options.projectId,
environment: options.environment,
secretValue: options.secretValue,
secretComment: options.secretComment,
secretPath: options.secretPath,
secretReminderNote: options.secretReminderNote,
secretReminderRepeatDays: options.secretReminderRepeatDays,
skipMultilineEncoding: options.skipMultilineEncoding,
tagIds: options.tagIds,
type: options.type,
});
} catch (err) {
throw newInfisicalError(err);
}
};
updateSecret = async (
secretName: DefaultApiApiV3SecretsRawSecretNamePatchRequest["secretName"],
options: UpdateSecretOptions
): Promise<UpdateSecretResult> => {
try {
const res = await this.#apiInstance.apiV3SecretsRawSecretNamePatch(
{
secretName,
apiV3SecretsRawSecretNamePatchRequest: {
...options,
workspaceId: options.projectId
}
},
this.#requestOptions
);
return res.data;
} catch (err) {
throw newInfisicalError(err);
}
};
createSecret = async (
secretName: DefaultApiApiV3SecretsRawSecretNamePostRequest["secretName"],
options: CreateSecretOptions
): Promise<CreateSecretResult> => {
try {
const res = await this.#apiInstance.apiV3SecretsRawSecretNamePost(
{
secretName,
apiV3SecretsRawSecretNamePostRequest: {
...options,
workspaceId: options.projectId
}
},
this.#requestOptions
);
return res.data;
} catch (err) {
throw newInfisicalError(err);
}
};
deleteSecret = async (
secretName: DefaultApiApiV3SecretsRawSecretNameDeleteRequest["secretName"],
options: DeleteSecretOptions
): Promise<DeleteSecretResult> => {
try {
const res = await this.#apiInstance.apiV3SecretsRawSecretNameDelete(
{
secretName,
apiV3SecretsRawSecretNameDeleteRequest: {
...options,
workspaceId: options.projectId
}
},
this.#requestOptions
);
return res.data;
} catch (err) {
throw newInfisicalError(err);
}
};
deleteSecret = async (secretName: string, options: DeleteSecretOptions) => {
try {
return await this.apiClient.deleteSecret(secretName, {
workspaceId: options.projectId,
environment: options.environment,
secretPath: options.secretPath,
type: options.type,
});
} catch (err) {
throw newInfisicalError(err);
}
};
}

View File

@@ -2,9 +2,7 @@ import axios from "axios";
import { AWS_IDENTITY_DOCUMENT_URI, AWS_TOKEN_METADATA_URI } from "./constants";
import AWS from "aws-sdk";
import { InfisicalSDKError } from "./errors";
import { ApiV3SecretsRawGet200Response } from "../infisicalapi_client";
type Secret = ApiV3SecretsRawGet200Response["secrets"][number];
import { Secret } from "../api/types";
export const getUniqueSecretsByKey = (secrets: Secret[]) => {
const secretMap = new Map<string, Secret>();