Skip to content

Commit

Permalink
fix: get the execution API based methods to check capabilities and re…
Browse files Browse the repository at this point in the history
…port server version info on error (#145)
  • Loading branch information
slewis74 authored Jan 27, 2023
1 parent a03c909 commit d0ce6f6
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 2 deletions.
24 changes: 24 additions & 0 deletions src/features/capabilities/capability.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Client } from "../../client";
import { processConfiguration } from "../../clientConfiguration.test";
import { checkForCapability } from "./capability";

describe("Check capabilities", () => {
let client: Client;

beforeAll(async () => {
client = await Client.create(processConfiguration());
console.log(`Client connected to API endpoint successfully.`);
});

test("Returns no error for something we know exists", async () => {
const result = await checkForCapability(client, "GetAllSpacesRequest", "2018.1");
expect(result).toBeNull();
});

test("Returns false for something we know does not exist", async () => {
const result = await checkForCapability(client, "SomeMadeUpRequest", "2023.1");
expect(result).toBe(
"The Octopus instance does not support SomeMadeUpRequest, it needs to be at least version 2023.1 to get access to the feature you are trying to use."
);
});
});
23 changes: 23 additions & 0 deletions src/features/capabilities/capability.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Client } from "../../client";
import { apiLocation } from "../../apiLocation";
import { OctopusError } from "../../octopusError";

interface CapabilitiesResponse {
Capabilities: string[];
}

export async function checkForCapability(client: Client, capabilityName: string, minimumVersionThisWouldAppearIn: string): Promise<string | null> {
try {
const response = await client.get<CapabilitiesResponse>(`${apiLocation}/capabilities`);
return response.Capabilities.filter((c) => c === capabilityName).length === 1
? null
: `The Octopus instance does not support ${capabilityName}, it needs to be at least version ${minimumVersionThisWouldAppearIn} to get access to the feature you are trying to use.`;
} catch (e) {
if (e instanceof OctopusError) {
if (e.StatusCode && e.StatusCode != 200) {
return `The Octopus instance does not support the Capabilities API, you will need to upgrade it at least 2022.3 to get access to the feature you are trying to use.`;
}
}
return `Unknown error occurred trying to determine if the Octopus instance supports ${capabilityName}.`;
}
}
1 change: 1 addition & 0 deletions src/features/capabilities/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./capability";
1 change: 1 addition & 0 deletions src/features/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./buildInformation";
export * from "./capabilities";
export * from "./deploymentEnvironments";
export * from "./forms";
export * from "./lifecycles";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Client } from "../../../../client";
import { ResourceCollection, ListArgs, spaceScopedRoutePrefix } from "../../../..";
import { ResourceCollection, ListArgs, spaceScopedRoutePrefix, checkForCapability } from "../../../..";
import { TaskState } from "../../../serverTasks";
import { Deployment } from "./deployment";
import {
Expand Down Expand Up @@ -53,6 +53,12 @@ export class DeploymentRepository {
}

async create(command: CreateDeploymentUntenantedCommandV1): Promise<CreateDeploymentUntenantedResponseV1> {
const capabilityError = await checkForCapability(this.client, "CreateDeploymentUntenantedCommandV1", "2022.3");
if (capabilityError) {
this.client.error?.(capabilityError);
throw new Error(capabilityError);
}

this.client.debug(`Deploying a release...`);

// WARNING: server's API currently expects there to be a SpaceIdOrName value, which was intended to allow use of names/slugs, but doesn't
Expand Down Expand Up @@ -81,6 +87,12 @@ export class DeploymentRepository {
}

async createTenanted(command: CreateDeploymentTenantedCommandV1): Promise<CreateDeploymentTenantedResponseV1> {
const capabilityError = await checkForCapability(this.client, "CreateDeploymentTenantedCommandV1", "2022.3");
if (capabilityError) {
this.client.error?.(capabilityError);
throw new Error(capabilityError);
}

this.client.debug(`Deploying a tenanted release...`);

// WARNING: server's API currently expects there to be a SpaceIdOrName value, which was intended to allow use of names/slugs, but doesn't
Expand Down
8 changes: 7 additions & 1 deletion src/features/projects/releases/releaseRepository.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Client } from "../../../client";
import { spaceScopedRoutePrefix } from "../../..";
import { checkForCapability, spaceScopedRoutePrefix } from "../../..";
import { CreateReleaseCommandV1 } from "./createReleaseCommandV1";
import { CreateReleaseResponseV1 } from "./createReleaseResponseV1";

Expand All @@ -13,6 +13,12 @@ export class ReleaseRepository {
}

async create(command: CreateReleaseCommandV1): Promise<CreateReleaseResponseV1> {
const capabilityError = await checkForCapability(this.client, "CreateReleaseCommandV1", "2022.3");
if (capabilityError) {
this.client.error?.(capabilityError);
throw new Error(capabilityError);
}

this.client.debug(`Creating a release...`);

// WARNING: server's API currently expects there to be a SpaceIdOrName value, which was intended to allow use of names/slugs, but doesn't
Expand Down
7 changes: 7 additions & 0 deletions src/features/projects/runbooks/runs/runbookRunRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { spaceScopedRoutePrefix } from "../../../../spaceScopedRoutePrefix";
import { ListArgs } from "../../../basicRepository";
import { ResourceCollection } from "../../../../resourceCollection";
import { CreateRunbookRunCommandV1, CreateRunbookRunResponseV1 } from "./createRunbookRunCommandV1";
import { checkForCapability } from "../../../capabilities";

// WARNING: we've had to do this to cover a mistake in Octopus' API. The API has been corrected to return PascalCase, but was returning camelCase
// for a number of versions, so we'll deserialize both and use whichever actually has a value
Expand Down Expand Up @@ -51,6 +52,12 @@ export class RunbookRunRepository {
}

async create(command: CreateRunbookRunCommandV1): Promise<CreateRunbookRunResponseV1> {
const capabilityError = await checkForCapability(this.client, "CreateRunbookRunCommandV1", "2022.3");
if (capabilityError) {
this.client.error?.(capabilityError);
throw new Error(capabilityError);
}

this.client.debug(`Running a runbook...`);

// WARNING: server's API currently expects there to be a SpaceIdOrName value, which was intended to allow use of names/slugs, but doesn't
Expand Down

0 comments on commit d0ce6f6

Please sign in to comment.