Skip to content
Open
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
74 changes: 37 additions & 37 deletions cli/oclif.manifest.json
Original file line number Diff line number Diff line change
@@ -1,42 +1,5 @@
{
"commands": {
"everywhere:base": {
"aliases": [],
"args": {},
"flags": {
"plugin-dir": {
"char": "D",
"description": "Plugin directory (defaults to current working directory).",
"name": "plugin-dir",
"hasDynamicHelp": false,
"multiple": false,
"type": "option"
},
"verbose": {
"char": "v",
"description": "Show detailed output.",
"name": "verbose",
"allowNo": false,
"type": "boolean"
}
},
"hasDynamicHelp": false,
"hidden": true,
"hiddenAliases": [],
"id": "everywhere:base",
"pluginAlias": "@workday/everywhere",
"pluginName": "@workday/everywhere",
"pluginType": "core",
"strict": true,
"enableJsonFlag": false,
"isESM": true,
"relativePath": [
"dist",
"commands",
"everywhere",
"base.js"
]
},
"everywhere:bind": {
"aliases": [],
"args": {
Expand Down Expand Up @@ -287,6 +250,43 @@
"publish.js"
]
},
"everywhere:unpublish": {
"aliases": [],
"args": {},
"description": "Unpublishes your plugin from the Workday plugin registry.",
"flags": {
"plugin-dir": {
"char": "D",
"description": "Plugin directory (defaults to current working directory).",
"name": "plugin-dir",
"hasDynamicHelp": false,
"multiple": false,
"type": "option"
},
"verbose": {
"char": "v",
"description": "Show detailed output.",
"name": "verbose",
"allowNo": false,
"type": "boolean"
}
},
"hasDynamicHelp": false,
"hiddenAliases": [],
"id": "everywhere:unpublish",
"pluginAlias": "@workday/everywhere",
"pluginName": "@workday/everywhere",
"pluginType": "core",
"strict": true,
"enableJsonFlag": false,
"isESM": true,
"relativePath": [
"dist",
"commands",
"everywhere",
"unpublish.js"
]
},
"everywhere:view": {
"aliases": [],
"args": {},
Expand Down
9 changes: 7 additions & 2 deletions cli/src/commands/everywhere/auth/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default class AuthTokenCommand extends EverywhereBaseCommand {
};

async run(): Promise<void> {
const { flags } = await this.parse(AuthTokenCommand);
const { flags } = await this.parseFlags();
const config = appConfig();
const saved = config.read();
const token = saved.auth?.token;
Expand All @@ -26,7 +26,7 @@ export default class AuthTokenCommand extends EverywhereBaseCommand {

const gateway = saved.auth?.gateway ?? DEFAULT_GATEWAY;
const scheme = (saved.auth?.https ?? DEFAULT_HTTPS) ? 'https' : 'http';
const url = `${scheme}://${gateway}/auth/token`;
const url = `${scheme}://${gateway}/api/v1/auth/token`;

let response: Response;
try {
Expand Down Expand Up @@ -66,4 +66,9 @@ export default class AuthTokenCommand extends EverywhereBaseCommand {
}
this.log((parsed as { token: string }).token);
}

protected async parseFlags(): Promise<{ flags: { json: boolean } }> {
const { flags } = await this.parse(AuthTokenCommand);
return { flags: { json: flags.json } };
}
}
13 changes: 5 additions & 8 deletions cli/src/commands/everywhere/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,14 @@ export default class PublishCommand extends EverywhereBaseCommand {
const manifest = this.loadManifest(pluginDir);

this.log('Bundling plugin...');
const { archive, slug } = await this.buildPluginArchive(manifest, pluginDir);
const { archive } = await this.buildPluginArchive(manifest, pluginDir);

this.log('Publishing plugin...');
const result = await this.publishPlugin({
gateway,
httpsEnabled: config.auth?.https ?? true,
token,
archivePath: archive.filePath,
appRefId: slug,
});

this.log(this.formatSuccessMessage(result, config));
Expand Down Expand Up @@ -86,14 +85,12 @@ export default class PublishCommand extends EverywhereBaseCommand {
private formatSuccessMessage(result: RegistryUploadResult, config: AppConfig): string {
const scheme = (config.auth?.https ?? true) ? 'https' : 'http';
return [
`Successfully published your plugin: ${result.referenceId}`,
`Successfully published your plugin: ${result.name}`,
'',
`Log into ${scheme}://${config.auth?.gateway}/builder/preview to view and deploy your app`,
`Bundle: ${scheme}://${config.auth?.gateway}${result.bundleUrl}`,
'',
`ID: ${result.id}`,
`Status: ${result.status}`,
`App type: ${result.appType}`,
`Created by: ${result.creator}`,
`Tenant: ${result.tenant}`,
`Title: ${result.title}`,
].join('\n');
}
}
54 changes: 54 additions & 0 deletions cli/src/commands/everywhere/unpublish.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { type AppConfig, appConfig } from '../../config.js';
import { readPluginManifest } from '../../manifest/manifest.js';
import { deleteFromRegistry } from '../../registry/registry.js';
import EverywhereBaseCommand from '../../lib/command.js';

export default class UnpublishCommand extends EverywhereBaseCommand {
static override description = 'Unpublishes your plugin from the Workday plugin registry.';

static override flags = {
...EverywhereBaseCommand.baseFlags,
};

async run(): Promise<void> {
const pluginDir = await this.parsePluginDir();
const config = appConfig().read();

const { gateway, token } = this.requireAuth(config);
const name = this.loadPluginName(pluginDir);

await this.unpublishPlugin({
gateway,
httpsEnabled: config.auth?.https ?? true,
token,
appId: name,
});

this.log(`Successfully unpublished plugin: ${name}`);
}

private requireAuth(config: AppConfig): { gateway: string; token: string } {
const gateway = config.auth?.gateway;
const token = config.auth?.token;
if (!gateway || !token) {
this.error('You must be logged in to unpublish your plugin');
}
return { gateway, token };
}

private loadPluginName(pluginDir: string): string {
try {
return readPluginManifest(pluginDir).name;
} catch (err) {
this.error(err instanceof Error ? err.message : 'Failed to read package.json');
}
}

private async unpublishPlugin(options: Parameters<typeof deleteFromRegistry>[0]): Promise<void> {
try {
await deleteFromRegistry(options);
} catch (err) {
this.error(err instanceof Error ? err.message : 'Failed to unpublish plugin');
}
}
}
2 changes: 1 addition & 1 deletion cli/src/commands/everywhere/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default class ViewCommand extends EverywhereBaseCommand {
? {}
: {
proxy: {
'/api/data/graphql': {
'/api/v1/data/graphql': {
target: apiServer,
changeOrigin: true,
configure: (proxy, _options) => {
Expand Down
2 changes: 1 addition & 1 deletion cli/src/data/vite-data-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function dataServicePlugin(pluginDir: string): VitePlugin {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
configureServer(server: { middlewares: { use: (...args: any[]) => void } }) {
server.middlewares.use(
'/api/data/graphql',
'/api/v1/data/graphql',
async (req: IncomingMessage, res: ServerResponse) => {
if (req.method !== 'POST') {
res.writeHead(405, { 'Content-Type': 'application/json' });
Expand Down
64 changes: 44 additions & 20 deletions cli/src/registry/registry.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
import * as fs from 'node:fs';
import * as path from 'node:path';

export interface RegistryUploadOptions {
gateway: string;
httpsEnabled: boolean;
token: string;
archivePath: string;
appRefId: string;
}

export interface RegistryUploadResult {
id: string;
referenceId: string;
status: string;
appType: string;
creator: string;
tenant: string;
name: string;
title: string;
bundleUrl: string;
}

const REGISTRY_UPLOAD_RESULT_KEYS: (keyof RegistryUploadResult)[] = [
'id',
'referenceId',
'status',
'appType',
'creator',
'tenant',
'name',
'title',
'bundleUrl',
];

function parseRegistryUploadResult(json: unknown): RegistryUploadResult {
Expand All @@ -49,26 +45,54 @@ function parseRegistryUploadResult(json: unknown): RegistryUploadResult {
return result;
}

export interface RegistryDeleteOptions {
gateway: string;
httpsEnabled: boolean;
token: string;
appId: string;
}

export async function deleteFromRegistry(options: RegistryDeleteOptions): Promise<void> {
const { gateway, httpsEnabled, token, appId } = options;

const scheme = httpsEnabled ? 'https' : 'http';
const url = new URL(`${scheme}://${gateway}/api/v1/app/${appId}`);

let response: Response;
try {
response = await fetch(url, {
method: 'DELETE',
headers: { Authorization: `Bearer ${token}` },
});
} catch (error: unknown) {
const message = error instanceof Error ? error.message : 'Unknown error occurred';
throw new Error(`Failed to unpublish plugin: ${message}`, { cause: error });
}

if (!response.ok) {
throw new Error('There was an error unpublishing your plugin from the registry');
}
}

export async function uploadToRegistry(
options: RegistryUploadOptions
): Promise<RegistryUploadResult> {
const { gateway, httpsEnabled, token, archivePath, appRefId } = options;
const { gateway, httpsEnabled, token, archivePath } = options;

const scheme = httpsEnabled ? 'https' : 'http';
const url = new URL(`${scheme}://${gateway}/builder/v1/apps/source/archive`);
const url = new URL(`${scheme}://${gateway}/api/v1/apps/publish`);

const blob = await fs.openAsBlob(archivePath, { type: 'application/zip' });
const filename = path.basename(archivePath);
const form = new FormData();
form.set('payload', new File([blob], filename, { type: 'application/zip' }));
form.set('appRefId', appRefId);

let response: Response;
try {
response = await fetch(url, {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
body: form,
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/zip',
},
body: blob,
});
} catch (error: unknown) {
const message = error instanceof Error ? error.message : 'Unknown error occurred';
Expand Down
Loading