From e7e96c1ae936d7705b2035c0ff76dc482cfea06b Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Sat, 12 Apr 2025 01:27:43 +0400 Subject: [PATCH] feat: create folders, environments, and projects --- README.md | 69 ++++++++++++++++++++++++++++++++++++++ src/custom/environments.ts | 33 ++++++++++++++++++ src/custom/folders.ts | 35 +++++++++++++++++++ src/custom/projects.ts | 30 +++++++++++++++++ src/index.ts | 15 +++++++++ test/index.ts | 63 ++++++++++------------------------ 6 files changed, 200 insertions(+), 45 deletions(-) create mode 100644 src/custom/environments.ts create mode 100644 src/custom/folders.ts create mode 100644 src/custom/projects.ts diff --git a/README.md b/README.md index c9dbeb8..8ef375d 100644 --- a/README.md +++ b/README.md @@ -432,3 +432,72 @@ const renewedLease = await client.dynamicSecrets().leases.renew(newLease.lease.i **Returns:** - `ApiV1DynamicSecretsLeasesLeaseIdDelete200Response`: The renewed lease response _(doesn't contain new credentials)_. +### `projects` + +#### Create a new project + +```typescript +const project = await client.projects().create({ + projectName: "", + type: "secret-manager", // cert-manager, secret-manager, kms, ssh + projectDescription: "", // Optional + slug: "", // Optional + template: "", // Optional + kmsKeyId: "kms-key-id" // Optional +}); +``` + +**Parameters:** +- `projectName` (string): The name of the project to create. +- `type` (string): The type of project to create. Valid options are `secret-manager`, `cert-manager`, `kms`, `ssh` +- `projectDescription` (string): An optional description of the project to create. +- `slug` (string): An optional slug for the project to create. If not provided, one will be generated automatically. +- `template` (string): Optionally provide a project template name to use for creating this project. +- `kmsKeyId` (string): The ID of the KMS key to use for the project. Will use the Infisical KMS by default. + +**Returns:** +- `ApiV1WorkspaceWorkspaceIdGet200ResponseWorkspace`: The project that was created. + +### `environments` + +#### Create a new environment + +```typescript +const environment = await client.environments().create({ + name: "Demo Environment", + projectId: "", + slug: "demo-environment", + position: 1 // Optional +}); +``` + +**Parameters:** +- `name` (string): The name of the environment to be created. +- `projectId` (string): The ID of the project to create the environment within. +- `slug`: (string): The slug of the environment to be created. +- `position` (number): An optional position of the environment to be created. The position is used in the Infisical UI to display environments in order. Environments with the lowest position come first. + +**Returns:** +- `ApiV1WorkspaceWorkspaceIdEnvironmentsEnvIdGet200ResponseEnvironment`: The environment that was created. + +#### Create a new folder + +```typescript +const folder = await client.folders().create({ + name: "", + path: "", + projectId: "", + environment: "", + description: "" // Optional +}); +``` + +**Parameters:** +- `name` (string): The name of the folder to create. +- `path` (string): The path where of where to create the folder. Defaults to `/`, which is the root folder. +- `projectId` (string): The ID of the project to create the folder within. +- `environment` (string): The slug of the environment to create the folder within. +- `description` (string): An optional folder description. + +**Returns:** +- `ApiV1FoldersPost200ResponseFolder`: The folder that was created. diff --git a/src/custom/environments.ts b/src/custom/environments.ts new file mode 100644 index 0000000..ee4c151 --- /dev/null +++ b/src/custom/environments.ts @@ -0,0 +1,33 @@ +import { RawAxiosRequestConfig } from "axios"; +import { DefaultApi as InfisicalApi } from "../infisicalapi_client"; +import type { ApiV1WorkspaceWorkspaceIdEnvironmentsPostRequest, ApiV1WorkspaceWorkspaceIdEnvironmentsPost200Response } from "../infisicalapi_client"; +import { newInfisicalError } from "./errors"; + +export type CreateEnvironmentOptions = { + projectId: string; +} & ApiV1WorkspaceWorkspaceIdEnvironmentsPostRequest; +export type CreateEnvironmentResult = ApiV1WorkspaceWorkspaceIdEnvironmentsPost200Response; + +export default class EnvironmentsClient { + #apiInstance: InfisicalApi; + #requestOptions: RawAxiosRequestConfig | undefined; + constructor(apiInstance: InfisicalApi, requestOptions: RawAxiosRequestConfig | undefined) { + this.#apiInstance = apiInstance; + this.#requestOptions = requestOptions; + } + + create = async (options: CreateEnvironmentOptions): Promise => { + try { + const res = await this.#apiInstance.apiV1WorkspaceWorkspaceIdEnvironmentsPost( + { + workspaceId: options.projectId, + apiV1WorkspaceWorkspaceIdEnvironmentsPostRequest: options + }, + this.#requestOptions + ); + return res.data.environment; + } catch (err) { + throw newInfisicalError(err); + } + }; +} diff --git a/src/custom/folders.ts b/src/custom/folders.ts new file mode 100644 index 0000000..30340d6 --- /dev/null +++ b/src/custom/folders.ts @@ -0,0 +1,35 @@ +import { RawAxiosRequestConfig } from "axios"; +import { DefaultApi as InfisicalApi } from "../infisicalapi_client"; +import type { ApiV1FoldersPostRequest, ApiV1FoldersPost200Response } from "../infisicalapi_client"; +import { newInfisicalError } from "./errors"; + +export type CreateFolderOptions = { + projectId: string; +} & Omit; +export type CreateFolderResult = ApiV1FoldersPost200Response; + +export default class FoldersClient { + #apiInstance: InfisicalApi; + #requestOptions: RawAxiosRequestConfig | undefined; + constructor(apiInstance: InfisicalApi, requestOptions: RawAxiosRequestConfig | undefined) { + this.#apiInstance = apiInstance; + this.#requestOptions = requestOptions; + } + + create = async (options: CreateFolderOptions): Promise => { + try { + const res = await this.#apiInstance.apiV1FoldersPost( + { + apiV1FoldersPostRequest: { + ...options, + workspaceId: options.projectId + } + }, + this.#requestOptions + ); + return res.data.folder; + } catch (err) { + throw newInfisicalError(err); + } + }; +} diff --git a/src/custom/projects.ts b/src/custom/projects.ts new file mode 100644 index 0000000..c5ef1fc --- /dev/null +++ b/src/custom/projects.ts @@ -0,0 +1,30 @@ +import { RawAxiosRequestConfig } from "axios"; +import { DefaultApi as InfisicalApi } from "../infisicalapi_client"; +import type { ApiV2WorkspacePost200Response, ApiV2WorkspacePostRequest } from "../infisicalapi_client"; +import { newInfisicalError } from "./errors"; + +export type CreateProjectOptions = ApiV2WorkspacePostRequest; +export type CreateProjectResult = ApiV2WorkspacePost200Response; + +export default class ProjectsClient { + #apiInstance: InfisicalApi; + #requestOptions: RawAxiosRequestConfig | undefined; + constructor(apiInstance: InfisicalApi, requestOptions: RawAxiosRequestConfig | undefined) { + this.#apiInstance = apiInstance; + this.#requestOptions = requestOptions; + } + + create = async (options: CreateProjectOptions): Promise => { + try { + const res = await this.#apiInstance.apiV2WorkspacePost( + { + apiV2WorkspacePostRequest: options + }, + this.#requestOptions + ); + return res.data.project; + } catch (err) { + throw newInfisicalError(err); + } + }; +} diff --git a/src/index.ts b/src/index.ts index 94ee7a1..64d5965 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,9 @@ import { RawAxiosRequestConfig } from "axios"; import DynamicSecretsClient from "./custom/dynamic-secrets"; import * as ApiClient from "./infisicalapi_client"; +import EnvironmentsClient from "./custom/environments"; +import ProjectsClient from "./custom/projects"; +import FoldersClient from "./custom/folders"; const buildRestClient = (apiClient: InfisicalApi, requestOptions?: RawAxiosRequestConfig) => { return { @@ -26,6 +29,9 @@ class InfisicalSDK { #requestOptions: RawAxiosRequestConfig | undefined; #secretsClient: SecretsClient; #dynamicSecretsClient: DynamicSecretsClient; + #environmentsClient: EnvironmentsClient; + #projectsClient: ProjectsClient; + #foldersClient: FoldersClient; #authClient: AuthClient; #basePath: string; @@ -41,6 +47,9 @@ class InfisicalSDK { this.#authClient = new AuthClient(this.authenticate.bind(this), this.#apiInstance); this.#dynamicSecretsClient = new DynamicSecretsClient(this.#apiInstance, this.#requestOptions); this.#secretsClient = new SecretsClient(this.#apiInstance, this.#requestOptions); + this.#environmentsClient = new EnvironmentsClient(this.#apiInstance, this.#requestOptions); + this.#projectsClient = new ProjectsClient(this.#apiInstance, this.#requestOptions); + this.#foldersClient = new FoldersClient(this.#apiInstance, this.#requestOptions); this.rest = () => buildRestClient(this.#apiInstance, this.#requestOptions); } @@ -62,11 +71,17 @@ class InfisicalSDK { this.#secretsClient = new SecretsClient(this.#apiInstance, this.#requestOptions); this.#dynamicSecretsClient = new DynamicSecretsClient(this.#apiInstance, this.#requestOptions); this.#authClient = new AuthClient(this.authenticate.bind(this), this.#apiInstance, accessToken); + this.#environmentsClient = new EnvironmentsClient(this.#apiInstance, this.#requestOptions); + this.#projectsClient = new ProjectsClient(this.#apiInstance, this.#requestOptions); + this.#foldersClient = new FoldersClient(this.#apiInstance, this.#requestOptions); return this; } secrets = () => this.#secretsClient; + environments = () => this.#environmentsClient; + projects = () => this.#projectsClient; + folders = () => this.#foldersClient; dynamicSecrets = () => this.#dynamicSecretsClient; auth = () => this.#authClient; rest = () => buildRestClient(this.#apiInstance, this.#requestOptions); diff --git a/test/index.ts b/test/index.ts index 725a7b0..481adbd 100644 --- a/test/index.ts +++ b/test/index.ts @@ -12,54 +12,27 @@ const PROJECT_ID = "PROJECT_ID"; clientSecret: "CLIENT_SECRET" }); - const allSecrets = await client.secrets().listSecrets({ - environment: "dev", - projectId: PROJECT_ID, - expandSecretReferences: true, - includeImports: false, - recursive: false + const environment = await client.environments().create({ + name: "Demo Environment", + projectId: "", + slug: "demo-environment", + position: 1 // Optional }); - console.log(allSecrets.secrets); - const singleSecret = await client.secrets().getSecret({ - environment: "dev", - projectId: PROJECT_ID, - secretName: "TEST1", - expandSecretReferences: true, // Optional - includeImports: true, // Optional - - type: "shared", // Optional - version: 1 // Optional + const project = await client.projects().create({ + projectName: "", + type: "secret-manager", // cert-manager, secret-manager, kms, ssh + projectDescription: "", // Optional + slug: "", // Optional + template: "", // Optional + kmsKeyId: "kms-key-id" // Optional }); - console.log(`Fetched single secret, ${singleSecret}=${singleSecret.secretValue}`); - const newSecret = await client.secrets().createSecret("NEW_SECRET_NAME22423423", { - environment: "dev", - projectId: PROJECT_ID, - secretValue: "SECRET_VALUE" + const folder = await client.folders().create({ + name: "", + path: "", + projectId: "", + environment: "", + description: "" // Optional }); - console.log(`You created a new secret: ${newSecret.secret.secretKey}`); - - const updatedSecret = await client.secrets().updateSecret("NEW_SECRET_NAME22423423", { - environment: "dev", - projectId: PROJECT_ID, - secretValue: "UPDATED_SECRET_VALUE", - newSecretName: "NEW_SECRET_NAME22222", // Optional - secretComment: "This is an updated secret", // Optional - - secretReminderNote: "This is an updated reminder note", // Optional - secretReminderRepeatDays: 14, // Optional - skipMultilineEncoding: false, // Optional - metadata: { - // Optional - extra: "metadata" - } - }); - console.log(`You updated the secret: ${updatedSecret.secret.secretKey}`); - - const deletedSecret = await client.secrets().deleteSecret("NEW_SECRET_NAME22222", { - environment: "dev", - projectId: PROJECT_ID - }); - console.log(`You deleted the secret: ${deletedSecret.secret.secretKey}`); })();