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
1 change: 1 addition & 0 deletions .ai/instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,4 @@ Reference these files for scaffolding new code:
- `.ai/skills/prompt.md` — Prompts class conventions; simple / standard / delegation / wizard variants
- `.ai/skills/service.md` — Infrastructure Service conventions; SDK controller / axios-auth / axios-stateless variants
- `.ai/skills/value-object.md` — Value object (rich class) conventions; encapsulation, boundary unwrapping, composition rules
- `.ai/skills/contribution-guide.md` — Contribution workflow: branching from `dev`, local debugging/packaging, README updates, releasing the CLI and Platform SDK, NPM tagging
85 changes: 85 additions & 0 deletions .ai/skills/contribution-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Contribution Guide

Please create your feature branch from the `dev` branch and add your changes. It is recommended to delete the local copy of the `dev` branch (if you have one) and pull the latest remote copy whenever you create your feature branch, this is needed because the `dev` branch is not a long-running branch and is deleted whenever we squash-and-merge to the release branch i.e. the `beta` branch.

```
Note: At the time of writing this Wiki, the CLI follows the `beta` release branch whereas the SDK follows the `alpha` release branch.
```

## Debugging the CLI Locally

The `.vscode/launch.json` file serves as your entry point to debugging the CLI locally. Add the command + flags you want to debug in the `"args"` of the `"configurations"` section and press the F5 key to start debugging in VS Code.

If you want to switch to the dev environment of the Platform API, add the `"env"` property to the `"configurations"` section as follows:

```json
"env": {
"NODE_TLS_REJECT_UNAUTHORIZED": "0",
"APIMATIC_BASE_URL": "https://app.dev.apimatic.io/api;https://auth.dev.apimatic.io;https://api.package-publishing.dev.apimatic.io/api"
},
```

If you're looking to run the CLI on dev environment, just set the `APIMATIC_BASE_URL` in your environment variables as mentioned above.

## Installing the Packaged CLI Locally

1. Run `pnpm install` and `pnpm run build` after making your changes.
2. Run `pnpm pack` to package the CLI, this will create a tar file in the repo.
3. Uninstall any existing installations by running `pnpm remove -g @apimatic/cli`.
4. Install the locally packaged CLI by running `pnpm add -g <file-name-of-tar-file>`.

## Updating the README

Whenever you add a new command or change the descriptions, flags, examples etc. properties within an existing command, remember to update the README using the command `oclif readme`. You need to install the `oclif` package first or run it on-demand by using the command `pnpm dlx oclif readme`. By default, this will generate the README changes automatically under the `<!-- usage -->` and `<!-- commands -->` comments in the README file.

## Releasing the CLI

1. Get your changes in your feature branch merged to the `dev` branch first.
2. Create a release PR targeting the `beta` branch from the `dev` branch.
3. *⚠️IMPORTANT⚠️* Before you can merge the `dev` branch to the `beta` branch, you must ask someone from the platform team to disable branch protection on the repo. We are using `semantic-release` package in our github action for releasing & publishing the CLI to NPM, and we have a limitation where `semantic-release` needs to create and push a commit to the `beta` branch right after release and create a tag for it. If branch protection is disabled, this step gets skipped and the release is incomplete.
4. Squash-and-merge your changes and verify the release github action succeeds.

## Updating the Latest Version on NPM for CLI after Release

Before you can tag the released version as the `latest` version on NPM, you need an auth token to authenticate with NPM. You will also need to be added to the `apimatic` organisation on NPM. Contact someone from the Platform team if you are unclear about these steps.

1. Switch to the `beta` branch and pull the latest changes after the release.
2. Update the `.npmrc` file to add the line `//registry.npmjs.org/:_authToken=<your-npm-auth-token>`. *⚠️DO NOT COMMIT THE AUTH TOKEN TO THE REPOSITORY⚠️*
3. Run the command `npm dist-tag add @apimatic/cli@<version-to-tag> latest`. Replace the `<version-to-tag>` placeholder with the actual version to tag as the latest, for e.g. `1.1.0-beta.10`.

## Updating the Platform SDK

Whenever you make any changes in the Platform API i.e. `apimatic-io` that are required in the CLI, like adding/updating an endpoint, you will need to update the Platform API's SDK by following these steps:

1. Update the Platform API spec in the [`apimatic-docs`](https://github.com/apimatic/apimatic-docs/tree/v3-master/src/apimatic-sdk-docs) repo.
2. Rename the `platform-sdk-generation-build.json` file to use it as your `APIMATIC-BUILD.json` file.
3. Generate the SDK for the build using the `apimatic sdk generate` command in `typescript`.
4. Create a feature branch in the [`apimatic-sdk-for-js`](https://github.com/apimatic/apimatic-sdk-for-js) repo. It is recommended to delete the local copy of the `dev` branch (if you have one) and pull the latest remote copy whenever you create your feature branch, this is needed because the `dev` branch is not a long-running branch and is deleted whenever we squash-and-merge to the release branch i.e. the `alpha` branch.
5. Copy the contents of the generated SDK to your feature branch. Ensure that the `package.json` does not contain any unintended changes, reach out to someone from the platform team if you are unsure.
6. Commit your changes and create a PR for merging your feature branch to the `dev` branch.

## Testing the Updated SDK Changes with the CLI Locally

1. Run `pnpm install` and `pnpm run build` in the SDK repo.
2. Package the SDK using the command `pnpm pack`. It is highly recommended to change the version in the `package.json` and set it to something like `0.2.0-alpha.5-test` with the word `test` appended to the end and the version incremented by one (usually the patch version is incremented in MAJOR.MINOR.PATCH).
3. Copy package tar file to the CLI repo where you have your feature branch open.
4. Update the `package.json` `"dependencies"` section, set the `"@apimatic/sdk": "file:apimatic-sdk-0.2.0-alpha.5-test.tgz"`, with the correct tar file name.
5. Update the `package.json` `"files"` property and add a new value in the array like `"/apimatic-sdk-0.2.0-alpha.5-test.tgz"`.
6. Run `pnpm install` and `pnpm run build`, and you can locally debug to verify your changes. Feel free to run `pnpm pack` in case you want to test the packaged CLI.
7. The packaged CLI build will contain the updated SDK.
8. When installing the packaged CLI, keep the sdk package tar file in the same folder/directory as the CLI (at the same level).

## Releasing the Platform SDK

1. Get your changes in your feature branch merged to the `dev` branch first.
2. Create a release PR targeting the `alpha` branch from the `dev` branch. Remember to create a proper commit message explaining the changes since the CLI is public-facing.
3. *⚠️IMPORTANT⚠️* Before you can merge the `dev` branch to the `alpha` branch, you must ask someone from the platform team to disable branch protection on the repo. We are using `semantic-release` package in our github action for releasing & publishing the SDK to NPM, and we have a limitation where `semantic-release` needs to create and push a commit to the `alpha` branch right after release and create a tag for it. If branch protection is disabled, this step gets skipped and the release is incomplete.
4. Squash-and-merge your changes and verify the release github action succeeds.

## Updating the Latest Version on NPM for Platform SDK after Release

Before you can tag the released version as the `latest` version on NPM, you need an auth token to authenticate with NPM. You will also need to be added to the `apimatic` organisation on NPM. Contact someone from the Platform team if you are unclear about these steps.

1. Switch to the `alpha` branch and pull the latest changes after the release.
2. Update the `.npmrc` file to add the line `//registry.npmjs.org/:_authToken=<your-npm-auth-token>`. *⚠️DO NOT COMMIT THE AUTH TOKEN TO THE REPOSITORY⚠️*
3. Run the command `npm dist-tag add @apimatic/sdk@<version-to-tag> latest`. Replace the `<version-to-tag>` placeholder with the actual version to tag as the latest, for e.g. `0.2.0-alpha.7`.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ Reference these files as needed for scaffolding:
- `.ai/skills/service.md` — Infrastructure Service conventions and scaffolding
- `.ai/skills/value-object.md` — Value object (rich class) conventions: encapsulation, boundary unwrapping, composition
- `.ai/skills/event.md` — Domain event conventions: past-tense naming, variants, where to fire
- `.ai/skills/contribution-guide.md` — Contribution workflow: branching from `dev`, local debugging/packaging, README updates, releasing the CLI and Platform SDK, NPM tagging
18 changes: 18 additions & 0 deletions src/infrastructure/service-error.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import axios from "axios";
import { ApiError } from "@apimatic/sdk";
import { format as f } from "../prompts/format.js";

export enum ServiceErrorCode {
Expand Down Expand Up @@ -30,6 +31,14 @@
static notFound(customMessage: string): ServiceError {
return new ServiceError(ServiceErrorCode.NotFound, customMessage, {});
}
static unauthorized(apiMessage: string | null): ServiceError {
const message = `${apiMessage ?? "You are not authorized to perform this action."} Please run ${f.cmdAlt(
"apimatic",
"auth",
"login"
)} to log in, or provide a valid auth key using the ${f.flag("auth-key")} flag.`;
return new ServiceError(ServiceErrorCode.UnAuthorized, message, {});
}

static readonly values: ServiceError[] = [
ServiceError.NotFound,
Expand All @@ -54,7 +63,16 @@
}
}

export function handleServiceError(error: unknown): ServiceError {

Check failure on line 66 in src/infrastructure/service-error.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 16 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=apimatic_apimatic-cli&issues=AZ9l2x7RpT2KYv7YQVJv&open=AZ9l2x7RpT2KYv7YQVJv&pullRequest=297
// SDK controllers throw typed `ApiError`s (not axios errors). The API reports
// the reason as {"message": "..."} deserialized into `result`.
if (error instanceof ApiError) {
const apiMessage = (error.result as { message?: string } | undefined)?.message ?? null;
if (error.statusCode === 401) return ServiceError.unauthorized(apiMessage);
if (error.statusCode === 404) return ServiceError.NotFound;
if (error.statusCode === 500) return ServiceError.ServerError;
}

if (axios.isAxiosError(error)) {
const status = error.response?.status;
if (status === 401) return ServiceError.UnAuthorized;
Expand Down
1 change: 1 addition & 0 deletions src/infrastructure/services/portal-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export class PortalService {
return err(ServiceError.forbidden(errorMessage));
}
}
// 401 (and other SDK ApiError statuses) are mapped centrally in handleServiceError.
const serviceError = handleServiceError(error);
return err(serviceError);
} finally {
Expand Down
Loading