Skip to content

Commit cd6972c

Browse files
authored
Adds alias for profile, improves error message (#344)
1 parent b58ba28 commit cd6972c

File tree

6 files changed

+69
-37
lines changed

6 files changed

+69
-37
lines changed

packages/tf-next/README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ tf-next deploy --endpoint <api-endpoint>
7373
The following options can be passed when using the `tf-next deploy` command:
7474

7575
- `--endpoint`
76-
- `--awsProfile`
76+
- `--profile` (also available as `--awsProfile`)
77+
AWS profile to use for authenticating against the API endpoint. Uses `default` profile if not specified.
7778

7879
### Deployment
7980

@@ -106,7 +107,8 @@ tf-next deployment rm <deployment-id> --force
106107
The following options can be passed when using the `tf-next deployment` command:
107108

108109
- `--endpoint`
109-
- `--awsProfile`
110+
- `--profile` (also available as `--awsProfile`)
111+
AWS profile to use for authenticating against the API endpoint. Uses `default` profile if not specified.
110112

111113
### Alias
112114

packages/tf-next/src/client/aws-profile.ts

+10-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { defaultProvider } from '@aws-sdk/credential-provider-node';
22
import { Credentials, MemoizedProvider } from '@aws-sdk/types';
33
import { Options, Arguments } from 'yargs';
44

5+
import { Client } from './client';
6+
57
type AwsCredentialProvider = MemoizedProvider<Credentials>;
68

79
type AWSProfileArguments = {
@@ -21,9 +23,13 @@ type AWSProfileArguments = {
2123
* @see {@link https://www.npmjs.com/package/@aws-sdk/credential-provider-node}
2224
*/
2325
const awsProfileMiddleware = (argv: Arguments): AwsCredentialProvider => {
24-
// If the --awsProfile flag is provided load a named profile
25-
const profile =
26-
typeof argv.awsProfile === 'string' ? argv.awsProfile : undefined;
26+
// If the --profile flag is provided load a named profile
27+
const profile = typeof argv.profile === 'string' ? argv.profile : undefined;
28+
29+
if (profile) {
30+
const client = argv.client as Client;
31+
client.output.debug(`Using AWS profile ${profile}`);
32+
}
2733

2834
return defaultProvider({
2935
profile,
@@ -38,6 +44,7 @@ const awsProfileMiddlewareOptions: Record<string, Options> = {
3844
type: 'string',
3945
description:
4046
'AWS profile that should be used for authentication with the API endpoint.',
47+
alias: 'awsProfile',
4148
},
4249
};
4350

packages/tf-next/src/client/services/api/api.ts

+37-29
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
createResponseError,
1313
ResponseError,
1414
} from '../../../utils/errors/response-error';
15+
import { CredentialsError } from '../../../utils/errors';
1516

1617
type NodeFetch = typeof nodeFetch;
1718
type CreateAliasRequestBody =
@@ -127,39 +128,46 @@ class ApiService {
127128
}
128129

129130
const parsedUrl = new URL(requestUrl, this.apiEndpoint);
130-
const signedRequest = await signature.sign({
131-
hostname: parsedUrl.hostname,
132-
protocol: parsedUrl.protocol,
133-
path: parsedUrl.pathname,
134-
headers: convertFetchHeaders({
135-
...fetchArgs[1]?.headers,
136-
host: parsedUrl.hostname,
137-
accept: 'application/json',
138-
}),
139-
method: fetchArgs[1]?.method?.toUpperCase() ?? 'GET',
140-
query: convertURLSearchParamsToQueryBag(parsedUrl.searchParams),
141-
body: fetchArgs[1]?.body,
142-
});
143-
const response = await nodeFetch(parsedUrl.href, {
144-
...fetchArgs[1],
145-
headers: signedRequest.headers,
146-
});
131+
try {
132+
const signedRequest = await signature.sign({
133+
hostname: parsedUrl.hostname,
134+
protocol: parsedUrl.protocol,
135+
path: parsedUrl.pathname,
136+
headers: convertFetchHeaders({
137+
...fetchArgs[1]?.headers,
138+
host: parsedUrl.hostname,
139+
accept: 'application/json',
140+
}),
141+
method: fetchArgs[1]?.method?.toUpperCase() ?? 'GET',
142+
query: convertURLSearchParamsToQueryBag(parsedUrl.searchParams),
143+
body: fetchArgs[1]?.body,
144+
});
145+
const response = await nodeFetch(parsedUrl.href, {
146+
...fetchArgs[1],
147+
headers: signedRequest.headers,
148+
});
149+
if (!response.ok) {
150+
throw await createResponseError(response);
151+
}
147152

148-
if (!response.ok) {
149-
throw await createResponseError(response);
150-
}
153+
// OK - parse the response
154+
if (response.headers.get('content-type') === 'application/json') {
155+
try {
156+
return (await response.json()) as Promise<T>;
157+
} catch (_ignoredError) {
158+
return {} as T;
159+
}
160+
}
151161

152-
// OK - parse the response
153-
if (response.headers.get('content-type') === 'application/json') {
154-
try {
155-
return (await response.json()) as Promise<T>;
156-
} catch (_ignoredError) {
157-
return {} as T;
162+
// Response from API is not OK, should never happen
163+
throw new Error('Invalid response from API');
164+
} catch (error: any) {
165+
if (error.name === 'CredentialsProviderError') {
166+
throw new CredentialsError();
158167
}
159-
}
160168

161-
// Response from API is not OK, should never happen
162-
throw new Error('Invalid response from API');
169+
throw error;
170+
}
163171
}
164172

165173
// Aliases

packages/tf-next/src/index.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,14 @@ async function runCli() {
1616
// give us access to the argv argument.
1717
// @see {@link https://github.com/yargs/yargs/issues/2133}
1818
(error, argv, output) => {
19-
if (error instanceof CliError) {
20-
const client = argv.client as Client;
19+
const client = argv.client as Client | undefined;
2120

21+
// Ensure that the output halts
22+
if (client) {
23+
client.output.stopSpinner();
24+
}
25+
26+
if (error instanceof CliError) {
2227
// Client should be initialized at this point
2328
if (!client) {
2429
throw new Error('Client was not initialized');

packages/tf-next/src/utils/errors/errors.ts

+10
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ export class MissingApiEndpoint extends CliError<'MISSING_API_ENDPOINT'> {
1010
}
1111
}
1212

13+
export class CredentialsError extends CliError<'INVALID_CREDENTIALS'> {
14+
constructor() {
15+
super({
16+
code: 'INVALID_CREDENTIALS',
17+
message:
18+
'Could not read the provided AWS credentials.\nPlease make sure that the provided AWS profile exists.',
19+
});
20+
}
21+
}
22+
1323
export class AliasOverrideNotAllowed extends CliError<'ALIAS_OVERRIDE_NOT_ALLOWED'> {
1424
constructor(alias: string) {
1525
super({

packages/tf-next/src/utils/errors/response-error.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ async function createResponseError(res: Response): Promise<ResponseError> {
4646
code: 'PERMISSION_ERROR',
4747
serverMessage: message,
4848
message:
49-
'Authentication failed. Make sure that the AWS profile is set correctly.',
49+
'Authentication failed.\nMake sure that the AWS user has the correct permissions.',
5050
});
5151
}
5252

0 commit comments

Comments
 (0)