Skip to content

Commit

Permalink
Pub/Sub: add wrangler pubsub commands (#1314)
Browse files Browse the repository at this point in the history
* Support for wrangler2 pubsub ns

* Added tests

* Changes for removing staging and enabling Prod API support

* More support for pubsub command

* Updates for prod

* pubsub: (wip) fix types, docs, and validation.

* pubsub: fix deletePubSubBrokers URL

* pubsub: fix on-publish-url flag

* pubsub: fix format strings

* pubsub: move issue,revoke,unrevoke under brokers

* pubsub: fix update output formatting

* pubsub: initial list tests

* pubsub: tests, fix delete

* pubsub: add public-keys, tests for ns

* pubsub: fix date parsing iterator; add beta warning

* pubsub: fix broker update + add test

* pubsub: support --expiration for token issue

* pubsub: separate commands

* pubsub: use URLSearchParams; fix tests

* update snapshots, remove examples folder

* fix merge on index.tsx

* reorder some files

Co-authored-by: netcli <[email protected]>
Co-authored-by: Oli Yu <[email protected]>
Co-authored-by: Sunil Pai <[email protected]>
  • Loading branch information
4 people authored Jun 27, 2022
1 parent 5cc0772 commit 2c996aa
Show file tree
Hide file tree
Showing 6 changed files with 1,241 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/wrangler/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ describe("wrangler", () => {
wrangler kv:bulk 💪 Interact with multiple Workers KV key-value pairs at once
wrangler pages ⚡️ Configure Cloudflare Pages
wrangler r2 📦 Interact with an R2 store
wrangler pubsub 📮 Interact and manage Pub/Sub Brokers
wrangler login 🔓 Login to Cloudflare
wrangler logout 🚪 Logout from Cloudflare
wrangler whoami 🕵️ Retrieve your user info and test your auth config
Expand Down Expand Up @@ -78,6 +79,7 @@ describe("wrangler", () => {
wrangler kv:bulk 💪 Interact with multiple Workers KV key-value pairs at once
wrangler pages ⚡️ Configure Cloudflare Pages
wrangler r2 📦 Interact with an R2 store
wrangler pubsub 📮 Interact and manage Pub/Sub Brokers
wrangler login 🔓 Login to Cloudflare
wrangler logout 🚪 Logout from Cloudflare
wrangler whoami 🕵️ Retrieve your user info and test your auth config
Expand Down
276 changes: 276 additions & 0 deletions packages/wrangler/src/__tests__/pubsub.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
import { setMockResponse, unsetAllMocks } from "./helpers/mock-cfetch";
import { mockConsoleMethods } from "./helpers/mock-console";
import { runInTempDir } from "./helpers/run-in-tmp";
import { runWrangler } from "./helpers/run-wrangler";
import type {
PubSubNamespace,
PubSubBroker,
PubSubBrokerUpdate,
PubSubBrokerOnPublish,
} from "../pubsub";

describe("wrangler", () => {
mockAccountId();
mockApiToken();
runInTempDir();
const std = mockConsoleMethods();

afterEach(() => {
unsetAllMocks();
});

describe("pubsub", () => {
describe("namespaces", () => {
describe("create", () => {
function mockCreateRequest(expectedNamespaceName: string) {
const requests = { count: 0 };
setMockResponse(
"/accounts/:accountId/pubsub/namespaces",
"POST",
([_url, accountId], { body }) => {
expect(accountId).toEqual("some-account-id");
const namespaceName = JSON.parse(body as string).name;
expect(namespaceName).toEqual(expectedNamespaceName);
requests.count += 1;
}
);
return requests;
}

it("should create a namespace", async () => {
const requests = mockCreateRequest("my-namespace");
await runWrangler("pubsub namespaces create my-namespace");
// TODO: check returned object
expect(requests.count).toEqual(1);
});
});

describe("list", () => {
function mockListRequest(namespaces: PubSubNamespace[]) {
const requests = { count: 0 };
setMockResponse(
"/accounts/:accountId/pubsub/namespaces",
([_url, accountId], init) => {
requests.count++;
expect(accountId).toEqual("some-account-id");
expect(init).toEqual({});
return { namespaces };
}
);
return requests;
}

it("should list namespaces", async () => {
const expectedNamespaces: PubSubNamespace[] = [
{ name: "namespace-1", created_on: "01-01-2001" },
{ name: "namespace-2", created_on: "01-01-2001" },
];
const requests = mockListRequest(expectedNamespaces);
await runWrangler("pubsub namespaces list");

expect(std.err).toMatchInlineSnapshot(`""`);
// TODO(elithrar): check returned object
expect(requests.count).toEqual(1);
});
});
});

describe("brokers", () => {
describe("create", () => {
function mockCreateRequest(expectedBrokerName: string) {
const requests = { count: 0 };
setMockResponse(
"/accounts/:accountId/pubsub/namespaces/:namespaceName/brokers",
"POST",
([_url, accountId, namespaceName], { body }) => {
expect(accountId).toEqual("some-account-id");
expect(namespaceName).toEqual("some-namespace");
const brokerName = JSON.parse(body as string).name;
expect(brokerName).toEqual(expectedBrokerName);
requests.count += 1;
}
);
return requests;
}

it("should create a broker", async () => {
const requests = mockCreateRequest("my-broker");
await runWrangler(
"pubsub brokers create my-broker --namespace=some-namespace"
);

// TODO: check returned object
expect(requests.count).toEqual(1);
});

it("fail to create broker when no namespace is set", async () => {
await expect(
runWrangler("pubsub brokers create my-broker")
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Missing required argument: namespace"`
);
});
});

describe("update", () => {
function mockUpdateRequest(
expectedBrokerName: string,
expectedExpiration: number,
expectedDescription: string,
expectedOnPublishConfig: PubSubBrokerOnPublish
) {
const requests = { count: 0 };
setMockResponse(
"/accounts/:accountId/pubsub/namespaces/:namespaceName/brokers/:brokerName",
"PATCH",
([_url, accountId, namespaceName, brokerName], { body }) => {
expect(accountId).toEqual("some-account-id");
expect(namespaceName).toEqual("some-namespace");
expect(brokerName).toEqual(expectedBrokerName);

const patchBody: PubSubBrokerUpdate = JSON.parse(body as string);
expect(patchBody.expiration).toEqual(expectedExpiration);
expect(patchBody.description).toEqual(expectedDescription);
expect(patchBody.on_publish).toEqual(expectedOnPublishConfig);

requests.count += 1;
}
);
return requests;
}

it("should update a broker's properties", async () => {
const expectedOnPublish: PubSubBrokerOnPublish = {
url: "https://foo.bar.example.com",
};
const requests = mockUpdateRequest(
"my-broker",
86400,
"hello",
expectedOnPublish
);
await runWrangler(
"pubsub brokers update my-broker --namespace=some-namespace --expiration=24h --description='hello' --on-publish-url='https://foo.bar.example.com'"
);

expect(std.err).toMatchInlineSnapshot(`""`);
// TODO(elithrar): check returned object
expect(requests.count).toEqual(1);
});
});

describe("list", () => {
function mockListRequest(brokers: PubSubBroker[]) {
const requests = { count: 0 };
setMockResponse(
"/accounts/:accountId/pubsub/namespaces/:namespaceName/brokers",
([_url, accountId, namespaceName], init) => {
requests.count++;
expect(accountId).toEqual("some-account-id");
expect(namespaceName).toEqual("some-namespace");
expect(init).toEqual({});
return { brokers };
}
);
return requests;
}

it("should list brokers", async () => {
const expectedBrokers: PubSubBroker[] = [
{ name: "broker-1", created_on: "01-01-2001" },
{ name: "broker-2", created_on: "01-01-2001" },
];
const requests = mockListRequest(expectedBrokers);
await runWrangler("pubsub brokers list --namespace=some-namespace");

expect(std.err).toMatchInlineSnapshot(`""`);
// TODO(elithrar): check returned object
expect(requests.count).toEqual(1);
});
});

describe("describe", () => {
function mockGetRequest(broker: PubSubBroker) {
const requests = { count: 0 };
setMockResponse(
"/accounts/:accountId/pubsub/namespaces/:namespaceName/brokers/:brokerName",
([_url, accountId, namespaceName, brokerName]) => {
requests.count++;
expect(accountId).toEqual("some-account-id");
expect(namespaceName).toEqual("some-namespace");
expect(brokerName).toEqual(broker.name);
return { result: broker };
}
);
return requests;
}

it("should describe a single broker", async () => {
const requests = mockGetRequest({ id: "1234", name: "my-broker" });
await runWrangler(
"pubsub brokers describe my-broker --namespace=some-namespace"
);

expect(std.err).toMatchInlineSnapshot(`""`);
// TODO(elithrar): check returned object
expect(requests.count).toEqual(1);
});
});

describe("issue", () => {
function mockIssueRequest(expectedBrokerName: string) {
const requests = { count: 0 };
setMockResponse(
"/accounts/:accountId/pubsub/namespaces/:namespaceName/brokers/:brokerName/credentials",
([_url, accountId, namespaceName, brokerName]) => {
expect(accountId).toEqual("some-account-id");
expect(namespaceName).toEqual("some-namespace");
expect(brokerName).toEqual(expectedBrokerName);
requests.count += 1;
}
);
return requests;
}

it("should issue a token for the broker", async () => {
const requests = mockIssueRequest("my-broker");
await runWrangler(
"pubsub brokers issue my-broker --namespace=some-namespace"
);

expect(std.err).toMatchInlineSnapshot(`""`);
// TODO(elithrar): check returned object
expect(requests.count).toEqual(1);
});
});

describe("public-keys", () => {
function mockIssueRequest(expectedBrokerName: string) {
const requests = { count: 0 };
setMockResponse(
"/accounts/:accountId/pubsub/namespaces/:namespaceName/brokers/:brokerName/publickeys",
([_url, accountId, namespaceName, brokerName]) => {
expect(accountId).toEqual("some-account-id");
expect(namespaceName).toEqual("some-namespace");
expect(brokerName).toEqual(expectedBrokerName);
requests.count += 1;
}
);
return requests;
}

it("should return the public keys for a broker", async () => {
const requests = mockIssueRequest("my-broker");
await runWrangler(
"pubsub brokers public-keys my-broker --namespace=some-namespace"
);

expect(std.err).toMatchInlineSnapshot(`""`);
// TODO(elithrar): check returned object
expect(requests.count).toEqual(1);
});
});
});
});
});
9 changes: 9 additions & 0 deletions packages/wrangler/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
} from "./parse";
import { previewHandler, previewOptions } from "./preview";
import publish from "./publish";
import { pubSubCommands } from "./pubsub/pubsub-commands";
import { createR2Bucket, deleteR2Bucket, listR2Buckets } from "./r2";
import { getAssetPaths, getSiteAssetPaths } from "./sites";
import {
Expand Down Expand Up @@ -1693,6 +1694,14 @@ function createCLIParser(argv: string[]) {
});
});

wrangler.command(
"pubsub",
"📮 Interact and manage Pub/Sub Brokers",
(pubsubYargs) => {
return pubSubCommands(pubsubYargs, subHelp);
}
);

/**
* User Group: login, logout, and whoami
* TODO: group commands into User group similar to .group() for flags in yargs
Expand Down
64 changes: 64 additions & 0 deletions packages/wrangler/src/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,67 @@ export function searchLocation(file: File, query: unknown): Location {
}
return { lineText, line, column, length, ...file };
}

const units = {
nanoseconds: 0.000000001,
nanosecond: 0.000000001,
microseconds: 0.000001,
microsecond: 0.000001,
milliseconds: 0.001,
millisecond: 0.001,
seconds: 1,
second: 1,
minutes: 60,
minute: 60,
hours: 3600,
hour: 3600,
days: 86400,
day: 86400,
weeks: 604800,
week: 604800,
month: 18144000,
year: 220752000,

nsecs: 0.000000001,
nsec: 0.000000001,
usecs: 0.000001,
usec: 0.000001,
msecs: 0.001,
msec: 0.001,
secs: 1,
sec: 1,
mins: 60,
min: 60,

ns: 0.000000001,
us: 0.000001,
ms: 0.001,
mo: 18144000,
yr: 220752000,

s: 1,
m: 60,
h: 3600,
d: 86400,
w: 604800,
y: 220752000,
};

/**
* Parse a human-readable time duration in seconds (including fractional)
*
* Invalid values will return NaN
*/
export function parseHumanDuration(s: string): number {
const unitsMap = new Map(Object.entries(units));
s = s.trim().toLowerCase();
let base = 1;
for (const [name, _] of unitsMap) {
if (s.endsWith(name)) {
s = s.substring(0, s.length - name.length);
base = unitsMap.get(name) || 1;
break;
}
}
return Number(s) * base;
}
Loading

0 comments on commit 2c996aa

Please sign in to comment.