Skip to content

Commit

Permalink
Merge pull request #175 from mittwald/feature/app-installation-shortid
Browse files Browse the repository at this point in the history
  • Loading branch information
martin-helmich authored Jan 25, 2024
2 parents 01340a8 + 300562a commit 5241fe0
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 17 deletions.
22 changes: 15 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1784,12 +1784,13 @@ Set context values for the current project, org or server

```
USAGE
$ mw context set [--project-id <value>] [--server-id <value>] [--org-id <value>]
$ mw context set [--project-id <value>] [--server-id <value>] [--org-id <value>] [--installation-id <value>]
FLAGS
--org-id=<value> ID or short ID of an organization
--project-id=<value> ID or short ID of a project
--server-id=<value> ID or short ID of a server
--installation-id=<value> ID or short ID of an app installation
--org-id=<value> ID or short ID of an organization
--project-id=<value> ID or short ID of a project
--server-id=<value> ID or short ID of a server
DESCRIPTION
Set context values for the current project, org or server
Expand Down Expand Up @@ -1936,12 +1937,14 @@ Create a new cron job

```
USAGE
$ mw cronjob create --description <value> --interval <value> [-i <value>] [-q] [--disable] [--email <value>]
[--url <value> | --command <value>] [--interpreter <value>]
$ mw cronjob create --description <value> --interval <value> [-p <value>] [-i <value>] [-q] [--disable] [--email
<value>] [--url <value> | --command <value>] [--interpreter <value>]
FLAGS
-i, --installation-id=<value> ID or short ID of an app installation; this flag is optional if a default app
installation is set in the context
-p, --project-id=<value> ID or short ID of a project; this flag is optional if a default project is set in the
context
-q, --quiet suppress process output and only display a machine-readable summary.
--command=<value> Command to execute for the cron job; either this or `--url` is required.
--description=<value> (required) Description of the cron job
Expand All @@ -1956,10 +1959,15 @@ FLAG DESCRIPTIONS
ID or short ID of an app installation; this flag is optional if a default app installation is set in the context
May contain a short ID or a full ID of an app installation; you can also use the "mw context set
May contain a short ID or a full ID of an app installation.; you can also use the "mw context set
--installation-id=<VALUE>" command to persistently set a default app installation for all commands that accept this
flag.
-p, --project-id=<value> ID or short ID of a project; this flag is optional if a default project is set in the context
May contain a short ID or a full ID of a project; you can also use the "mw context set --project-id=<VALUE>" command
to persistently set a default project for all commands that accept this flag.
-q, --quiet suppress process output and only display a machine-readable summary.
This flag controls if you want to see the process output or only a summary. When using mw non-interactively (e.g. in
Expand Down
2 changes: 1 addition & 1 deletion src/ExtendedBaseCommand.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export abstract class ExtendedBaseCommand<
}

public async withAppInstallationId(
command: CommandType<"installation"> | "flag" | "arg",
command: CommandType<"installation" | "project"> | "flag" | "arg",
): Promise<string> {
return withAppInstallationId(
this.apiClient,
Expand Down
15 changes: 15 additions & 0 deletions src/commands/context/set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
normalizeProjectIdToUuid,
normalizeServerIdToUuid,
} from "../../Helpers.js";
import { normalizeAppInstallationId } from "../../lib/app/flags.js";

export class Set extends BaseCommand {
static summary = "Set context values for the current project, org or server";
Expand All @@ -21,6 +22,10 @@ export class Set extends BaseCommand {
"org-id": Flags.string({
description: "ID or short ID of an organization",
}),
"installation-id": Flags.string({
description: "ID or short ID of an app installation",
aliases: ["app-id", "app-installation-id"],
}),
};

public async run(): Promise<void> {
Expand Down Expand Up @@ -53,5 +58,15 @@ export class Set extends BaseCommand {
await ctx.setOrgId(orgId);
this.log(`Set organization ID to ${orgId}`);
}

if (flags["installation-id"]) {
const installationId = await normalizeAppInstallationId(
this.apiClient,
(await ctx.projectId())?.value ?? "",
flags["installation-id"],
);
await ctx.setAppInstallationId(installationId);
this.log(`Set installation ID to ${installationId}`);
}
}
}
27 changes: 24 additions & 3 deletions src/lib/app/flags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import React from "react";
import { Text } from "ink";
import { assertStatus } from "@mittwald/api-client-commons";
import { ProcessRenderer } from "../../rendering/process/process.js";
import { projectFlags } from "../project/flags.js";
import { makeProjectFlagSet, projectFlags } from "../project/flags.js";
import {
ProcessFlags,
processFlags,
Expand All @@ -19,13 +19,34 @@ import {
OutputFlags,
} from "@oclif/core/lib/interfaces/parser.js";
import { generatePasswordWithSpecialChars } from "../password.js";
import { makeFlagSet } from "../context_flags.js";
import { isUuid } from "../../Helpers.js";

export const {
flags: appInstallationFlags,
args: appInstallationArgs,
withId: withAppInstallationId,
} = makeFlagSet("installation", "i", { displayName: "app installation" });
} = makeProjectFlagSet("installation", "i", {
displayName: "app installation",
normalize: normalizeAppInstallationId,
supportsContext: true,
});

export async function normalizeAppInstallationId(
apiClient: MittwaldAPIV2Client,
projectId: string,
id: string,
): Promise<string> {
if (isUuid(id)) {
return id;
}

const appInstallations = await apiClient.app.listAppinstallations({
projectId,
});
assertStatus(appInstallations, 200);

return appInstallations.data.find((inst) => inst.shortId === id)?.id ?? id;
}

export type AvailableFlagName = keyof AvailableFlags;

Expand Down
3 changes: 3 additions & 0 deletions src/lib/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,11 @@ export class Context {
public setProjectId = (id: string) => this.setContextValue("project-id", id);
public setServerId = (id: string) => this.setContextValue("server-id", id);
public setOrgId = (id: string) => this.setContextValue("org-id", id);
public setAppInstallationId = (id: string) =>
this.setContextValue("installation-id", id);

public projectId = () => this.getContextValue("project-id");
public serverId = () => this.getContextValue("server-id");
public orgId = () => this.getContextValue("org-id");
public appInstallationId = () => this.getContextValue("installation-id");
}
25 changes: 20 additions & 5 deletions src/lib/project/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export type ProjectFlagSetOpts = {
normalize: SubNormalizeFn;
shortIDName: string;
displayName: string;
supportsContext: boolean;
};

export function makeProjectFlagSet<TName extends ContextNames>(
Expand All @@ -40,27 +41,41 @@ export function makeProjectFlagSet<TName extends ContextNames>(
normalize = (_1, _2, id) => id,
shortIDName = "short ID",
displayName = name,
supportsContext = false,
} = opts;
const article = displayName.match(/^[aeiou]/i) ? "an" : "a";

const flagName: ContextKey<TName> = `${name}-id`;
const flags = {
...projectFlags,
[flagName]: Flags.string({
char,
required: true,
summary: `ID or ${shortIDName} of a ${displayName}`,
description: `May contain a ${shortIDName} or a full ID of a ${displayName}.`,
required: !supportsContext,
summary: `ID or ${shortIDName} of ${article} ${displayName}`,
description: `May contain a ${shortIDName} or a full ID of ${article} ${displayName}.`,
default: undefined,
}),
} as ContextFlags<TName | "project">;

const args = {
[flagName]: Args.string({
description: `ID or ${shortIDName} of a ${displayName}`,
required: true,
description: `ID or ${shortIDName} of ${article} ${displayName}`,
required: !supportsContext,
}),
} as ContextArgs<TName | "project">;

if (supportsContext) {
flags[
flagName
].summary += `; this flag is optional if a default ${displayName} is set in the context`;
flags[
flagName
].description += `; you can also use the "<%= config.bin %> context set --${flagName}=<VALUE>" command to persistently set a default ${displayName} for all commands that accept this flag.`;
args[
flagName
].description += `; this argument is optional if a default ${displayName} is set in the context`;
}

const idFromArgsOrFlag = (
flags: FlagOutput,
args: ArgOutput,
Expand Down
2 changes: 1 addition & 1 deletion src/rendering/react/RenderBaseCommand.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export abstract class RenderBaseCommand<
protected abstract render(): ReactNode;

protected useAppInstallationId(
command: CommandType<"installation"> | "flag" | "arg",
command: CommandType<"installation" | "project"> | "flag" | "arg",
): string {
return usePromise(() => this.withAppInstallationId(command), []);
}
Expand Down

0 comments on commit 5241fe0

Please sign in to comment.