Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 76 additions & 62 deletions examples/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createDigitalOceanClient } from "../src/dots/digitalOceanClient.js";
import { DigitalOceanApiKeyAuthenticationProvider } from '../src/dots/DigitalOceanApiKeyAuthenticationProvider.js';
import { v4 as uuidv4 } from 'uuid';
import dotenv from 'dotenv';
dotenv.config();
dotenv.config({ path: "../.env" });
const token = process.env.DIGITALOCEAN_TOKEN;
if (!token) {
throw new Error("DIGITALOCEAN_TOKEN not set");
Expand All @@ -21,6 +21,8 @@ async function main() {
throw new Error("SSH_KEY_NAME not set");
}
const sshKey = await findSshKey(keyName);
if (!sshKey.fingerprint)
throw new Error("SSH key fingerprint is undefined or null");
const dropletReq = {
name: `test-${uuidv4()}`,
region: REGION,
Expand All @@ -38,7 +40,7 @@ async function main() {
filesystemType: "ext4",
};
const volume = await createVolume(volumeReq);
console.log("Volume created: ", volume.id);
console.log("Volume created: ", (volume.id));
const volumeActionReq = {
dropletId: droplet.id,
type: "attach",
Expand Down Expand Up @@ -68,63 +70,6 @@ async function main() {
console.error(err);
}
}
async function waitForAction(id, wait = 5) {
console.log(`Waiting for action ${id} to complete...`, "", { flush: true });
let status = "in-progress";
while (status === "in-progress") {
try {
const resp = await client.v2.actions.byAction_id(id).get();
if (resp && resp.action) {
status = resp.action.status;
}
else {
throw new Error("Response or action is undefined");
}
if (status === "in-progress") {
process.stdout.write(".");
await new Promise(resolve => setTimeout(resolve, wait * 1000));
}
else if (status === "errored") {
throw new Error(`${resp.action.type} action ${resp.action.id} ${status}`);
}
else {
console.log(".");
}
}
catch (err) {
if (err instanceof Error && 'statusCode' in err) {
const httpError = err;
throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`);
}
else {
throw err;
}
}
}
}
async function createVolume(req) {
console.log(`Creating volume using: ${JSON.stringify(req)}`);
try {
const resp = await client.v2.volumes.post(req);
if (resp && resp.volume) {
const volume = resp.volume;
console.log(`Created volume ${volume.name} <ID: ${volume.id}>`);
return volume;
}
else {
throw new Error("Failed to create volume or volume is undefined");
}
}
catch (err) {
if (err instanceof Error && 'statusCode' in err) {
const httpError = err;
throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`);
}
else {
throw err;
}
}
}
async function findSshKey(name) {
console.log(`Looking for SSH key named ${name}...`);
let paginated = true;
Expand All @@ -134,7 +79,6 @@ async function findSshKey(name) {
if (resp && resp.sshKeys) {
for (const k of resp.sshKeys) {
if (k.name === name) {
console.log(`Found SSH key: ${k.fingerprint}`);
return k;
}
}
Expand Down Expand Up @@ -166,7 +110,7 @@ async function findSshKey(name) {
}
throw new Error("No SSH key found");
}
async function createDroplet(req = {}) {
async function createDroplet(req) {
console.log(`Creating Droplet using: ${JSON.stringify(req)}`);
try {
const resp = await client.v2.droplets.post(req);
Expand Down Expand Up @@ -196,7 +140,12 @@ async function createDroplet(req = {}) {
}
}
console.log(`Droplet ID: ${dropletId} Name: ${droplet.name} IP: ${ipAddress}`);
return droplet;
if (droplet?.id == null) {
throw new Error("Droplet ID is null or undefined");
}
return {
id: droplet.id,
};
}
else {
throw new Error("Failed to retrieve droplet details or networks information");
Expand All @@ -220,4 +169,69 @@ async function createDroplet(req = {}) {
}
}
}
async function createVolume(req) {
console.log(`Creating volume using: ${JSON.stringify(req)}`);
try {
const resp = await client.v2.volumes.post(req);
if (resp && resp.volume) {
const volume = resp.volume;
console.log(`Created volume ${volume.name} <ID: ${volume.id}>`);
const volumeData = {
id: volume.id ?? "",
name: volume.name ?? "",
sizeGigabytes: volume.sizeGigabytes ?? 0,
description: volume.description ?? "",
region: String(volume.region) ?? "",
filesystemType: volume.filesystemType ?? "",
};
return volumeData;
}
else {
throw new Error("Failed to create volume or volume is undefined");
}
}
catch (err) {
if (err instanceof Error && 'statusCode' in err) {
const httpError = err;
throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`);
}
else {
throw err;
}
}
}
async function waitForAction(id, wait = 5) {
console.log(`Waiting for action ${id} to complete...`, "", { flush: true });
let status = "in-progress";
while (status === "in-progress") {
try {
const resp = await client.v2.actions.byAction_id(id).get();
if (resp && resp.action) {
status = resp.action.status;
}
else {
throw new Error("Response or action is undefined");
}
if (status === "in-progress") {
process.stdout.write(".");
await new Promise(resolve => setTimeout(resolve, wait * 1000));
}
else if (status === "errored") {
throw new Error(`${resp.action.type} action ${resp.action.id} ${status}`);
}
else {
console.log(".");
}
}
catch (err) {
if (err instanceof Error && 'statusCode' in err) {
const httpError = err;
throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`);
}
else {
throw err;
}
}
}
}
main();
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 7 additions & 65 deletions tests/integration/actions.test.js
Original file line number Diff line number Diff line change
@@ -1,75 +1,17 @@
import nock from "nock";
import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary";
import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js";
import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js";
const token = "mock-token";
import dotenv from "dotenv";
dotenv.config();
const token = process.env.DIGITALOCEAN_TOKEN;
if (!token) {
throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file.");
}
const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token);
const adapter = new FetchRequestAdapter(authProvider);
const client = createDigitalOceanClient(adapter);
describe("Integration Test for Actions Endpoint", () => {
it("should list and get actions", async () => {
// List actions
const expected = {
actions: [
{
id: 36804636,
status: "completed",
type: "create",
started_at: "2020-11-14T16:29:21Z",
completed_at: "2020-11-14T16:30:06Z",
resource_id: 3164444,
resource_type: "droplet",
region: {
name: "New York 3",
slug: "nyc3",
features: [
"private_networking",
"backups",
"ipv6",
"metadata",
"install_agent",
"storage",
"image_transfer",
],
available: true,
sizes: [
"s-1vcpu-1gb",
"s-1vcpu-2gb",
"s-1vcpu-3gb",
"s-2vcpu-2gb",
"s-3vcpu-1gb",
"s-2vcpu-4gb",
"s-4vcpu-8gb",
"s-6vcpu-16gb",
"s-8vcpu-32gb",
"s-12vcpu-48gb",
"s-16vcpu-64gb",
"s-20vcpu-96gb",
"s-24vcpu-128gb",
"s-32vcpu-192g",
],
},
region_slug: "nyc3",
},
],
links: {
pages: {
pages: {
first: "https://api.digitalocean.com/v2/account/keys?page=1",
prev: "https://api.digitalocean.com/v2/account/keys?page=2",
},
},
},
meta: {
total: 1,
},
};
nock("https://api.digitalocean.com")
.get("/v2/actions")
.reply(200, expected);
nock("https://api.digitalocean.com")
.get(`/v2/actions/${expected.actions[0].id}`)
.reply(200, { action: expected.actions[0] });
const listResp = await client.v2.actions.get();
expect(listResp).not.toBeNull();
expect(listResp?.actions).toBeDefined();
Expand All @@ -80,5 +22,5 @@ describe("Integration Test for Actions Endpoint", () => {
expect(getResp).not.toBeNull();
expect(getResp?.action).toBeDefined();
expect(getResp?.action?.id).toBe(actionId);
});
}, 50000);
});
45 changes: 2 additions & 43 deletions tests/integration/billing.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import nock from "nock";
import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary";
import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js";
import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js";
import * as fs from "fs";
const invoiceUuid = "mock-invoice-uuid";
const baseUrl = 'https://api.digitalocean.com';
const invoiceUuid = "something";
import dotenv from "dotenv";
dotenv.config();
const token = process.env.DIGITALOCEAN_TOKEN;
Expand All @@ -15,65 +13,33 @@ const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token);
const adapter = new FetchRequestAdapter(authProvider);
const client = createDigitalOceanClient(adapter);
describe("Integration Test for Billing", () => {
beforeEach(() => {
nock.cleanAll();
});
it("should get customer balance", async () => {
const expectedResponse = { account_balance: "0.00" };
nock(baseUrl)
.get("/v2/customers/my/balance")
.reply(200, expectedResponse);
const getResp = await client.v2.customers.my.balance.get();
console.log(getResp);
expect(getResp).not.toBeNull();
expect(getResp?.accountBalance).toBe("0.00");
});
it("should list billing history", async () => {
const expectedResponse = {
billing_history: [
{ type: "Invoice" },
{ type: "Payment" },
],
};
nock(baseUrl)
.get("/v2/customers/my/billing_history")
.reply(200, expectedResponse);
const getResp = await client.v2.customers.my.billing_history.get();
expect(getResp).not.toBeNull();
expect(getResp?.billingHistory).toBeDefined();
expect(getResp?.billingHistory?.[0]?.type === "Invoice" ||
getResp?.billingHistory?.[0]?.type === "Payment").toBeTruthy();
});
it("should list invoices", async () => {
const expectedResponse = {
billing_history: [
{ type: "Invoice" },
{ type: "Payment" },
],
};
nock(baseUrl)
.get("/v2/customers/my/billing_history")
.reply(200, expectedResponse);
const getResp = await client.v2.customers.my.billing_history.get();
expect(getResp).not.toBeNull();
expect(getResp?.billingHistory).toBeDefined();
expect(getResp?.billingHistory?.[0]?.type === "Invoice" ||
getResp?.billingHistory?.[0]?.type === "Payment").toBeTruthy();
});
it("should get invoice CSV by UUID", async () => {
const expectedResponse = "product,group_description,";
nock(baseUrl)
.get(`/v2/customers/my/invoices/${invoiceUuid}/csv`)
.reply(200, expectedResponse);
const getResp = await client.v2.customers.my.invoices.byInvoice_uuid(invoiceUuid).csv.get();
expect(getResp).not.toBeNull();
const decodedResp = new TextDecoder('utf-8').decode(getResp);
expect(decodedResp).toContain("product,group_description,");
});
it("should get invoice PDF by UUID", async () => {
const expectedResponse = Buffer.from("mock-pdf-content");
nock(baseUrl)
.get(`/v2/customers/my/invoices/${invoiceUuid}/pdf`)
.reply(200, expectedResponse);
const getResp = await client.v2.customers.my.invoices.byInvoice_uuid(invoiceUuid).pdf.get();
expect(getResp).not.toBeNull();
const decodedResp = new TextDecoder('utf-8').decode(getResp);
Expand All @@ -83,14 +49,7 @@ describe("Integration Test for Billing", () => {
fs.unlinkSync(filePath);
});
it("should get invoice summary by UUID", async () => {
const expectedResponse = {
user_company: "DigitalOcean",
};
nock(baseUrl)
.get(`/v2/customers/my/invoices/${invoiceUuid}/summary`)
.reply(200, expectedResponse);
const getResp = await client.v2.customers.my.invoices.byInvoice_uuid(invoiceUuid).summary.get();
expect(getResp).not.toBeNull();
expect(getResp?.userCompany).toBe("DigitalOcean");
});
});
Loading
Loading