Skip to content

Commit

Permalink
Merge pull request #78 from subsquid/develop
Browse files Browse the repository at this point in the history
Release 2.9.0
  • Loading branch information
belopash authored Mar 12, 2024
2 parents 300ff47 + db9b580 commit 9618f98
Show file tree
Hide file tree
Showing 31 changed files with 582 additions and 716 deletions.
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@subsquid/cli",
"description": "squid cli tool",
"version": "2.8.7",
"version": "2.9.0",
"license": "GPL-3.0-or-later",
"repository": "[email protected]:subsquid/squid-cli.git",
"publishConfig": {
Expand Down Expand Up @@ -75,6 +75,8 @@
"@types/lodash": "^4.14.202",
"@types/targz": "^1.0.4",
"async-retry": "^1.3.3",
"axios": "^1.6.7",
"axios-retry": "^4.0.0",
"blessed-contrib": "^4.11.0",
"chalk": "^4.1.2",
"cli-select": "^1.1.2",
Expand All @@ -84,12 +86,13 @@
"fast-levenshtein": "^3.0.0",
"figlet": "^1.7.0",
"form-data": "^4.0.0",
"glob": "^10.3.10",
"ignore": "^5.3.1",
"inquirer": "^8.2.5",
"js-yaml": "^4.1.0",
"lodash": "^4.17.21",
"ms": "^2.1.3",
"neo-blessed": "^0.2.0",
"node-fetch": "^2.6.7",
"pretty-bytes": "^5.6.0",
"qs": "^6.11.2",
"reblessed": "^0.2.1",
Expand All @@ -111,7 +114,6 @@
"@types/js-yaml": "^4.0.9",
"@types/ms": "^0.7.34",
"@types/node": "^20.11.17",
"@types/node-fetch": "^2.6.11",
"@types/qs": "^6.9.11",
"@types/split2": "^3.2.1",
"@typescript-eslint/eslint-plugin": "^6.21.0",
Expand Down
12 changes: 10 additions & 2 deletions src/api/alias.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { api } from './api';
import { HttpResponse, SquidResponse } from './types';

export async function setProduction(squidName: string, versionName: string): Promise<SquidResponse> {
export async function setProduction({
orgCode,
squidName,
versionName,
}: {
orgCode: string;
squidName: string;
versionName: string;
}): Promise<SquidResponse> {
const { body } = await api<HttpResponse<SquidResponse>>({
method: 'put',
path: `/aliases/squid/${squidName}/versions/${versionName}/prod`,
path: `/orgs/${orgCode}/squids/${squidName}/versions/${versionName}/prod`,
});

return body.payload;
Expand Down
94 changes: 50 additions & 44 deletions src/api/api.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import path from 'path';

import axios, { Method } from 'axios';
import axiosRetry, { IAxiosRetryConfig, isNetworkOrIdempotentRequestError } from 'axios-retry';
import chalk from 'chalk';
import { pickBy } from 'lodash';
import fetch from 'node-fetch';
import qs from 'qs';
import ms from 'ms';

import { getConfig } from '../config';

const API_DEBUG = process.env.API_DEBUG === 'true';

const DEFAULT_RETRY: IAxiosRetryConfig = {
retries: 10,
retryDelay: axiosRetry.exponentialDelay,
retryCondition: isNetworkOrIdempotentRequestError,
};

axiosRetry(axios, DEFAULT_RETRY);

let version = 'unknown';
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
Expand All @@ -17,7 +26,7 @@ try {

export class ApiError extends Error {
constructor(
public status: number,
public request: { status: number; method: string; url: string },
public body: {
error: string;
message?: string;
Expand All @@ -43,81 +52,78 @@ export async function api<T = any>({
path,
data,
query = {},
headers = {},
auth,
responseType = 'json',
abortController,
retry,
}: {
method: 'get' | 'post' | 'put' | 'delete' | 'patch';
method: Method;
path: string;
query?: Record<string, string | string[] | boolean | number | undefined>;
data?: unknown;
headers?: Record<string, string>;
auth?: { apiUrl: string; credentials: string };
responseType?: 'json' | 'stream';
abortController?: AbortController;
retry?: number;
}): Promise<{ body: T }> {
const config = auth || getConfig();

const sanitizedQuery = pickBy(query, (v) => v);
const queryString = Object.keys(sanitizedQuery).length ? `?${qs.stringify(sanitizedQuery)}` : '';

const url = !path.startsWith('https') ? `${config.apiUrl}${path}${queryString}` : `${path}${queryString}`;
const started = Date.now();
// add the API_URL to the path if it's not a full url
const url = !path.startsWith('https') ? `${config.apiUrl}${path}` : path;

const headers = {
'Content-Type': 'application/json',
authorization: `token ${config.credentials}`,
const finalHeaders = {
authorization: url.startsWith(config.apiUrl) ? `token ${config.credentials}` : null,
'X-CLI-Version': version,
...headers,
};

if (API_DEBUG) {
console.log(
chalk.dim(new Date().toISOString()),
chalk.cyan`[HTTP REQUEST]`,
chalk.dim(method?.toUpperCase()),
url,
chalk.dim(JSON.stringify({ headers })),
);
if (data) {
console.log(chalk.dim(JSON.stringify(data)));
}
}
const response = await fetch(url, {
const response = await axios(url, {
method,
headers,
body: data ? JSON.stringify(data) : undefined,
headers: finalHeaders,
data,
timeout: responseType === 'stream' ? 0 : undefined,
responseType,
params: pickBy(query, (v) => v),
signal: abortController ? (abortController.signal as any) : undefined,
validateStatus: () => true,
'axios-retry': retry
? {
...DEFAULT_RETRY,
retries: retry,
}
: undefined,
});

let body;
if (responseType === 'json') {
const rawBody = await response.text();
try {
body = responseType === 'json' ? JSON.parse(rawBody) : response.body;
} catch (e) {
body = rawBody;
}
} else {
body = response.body;
}

if (API_DEBUG) {
console.log(
chalk.dim(new Date().toISOString()),
chalk.cyan`[HTTP RESPONSE]`,
url,
chalk.cyan`[${method.toUpperCase()}]`,
response.config.url,
chalk.cyan(response.status),
ms(Date.now() - started),
chalk.dim(JSON.stringify({ headers: response.headers })),
);
if (body && responseType === 'json') {
console.log(chalk.dim(JSON.stringify(body, null, 2)));
if (response.data && responseType === 'json') {
console.log(chalk.dim(JSON.stringify(response.data)));
}
}

switch (response.status) {
case 200:
case 201:
return { body };
case 204:
return { body: response.data };
default:
throw new ApiError(response.status, body as any);
throw new ApiError(
{
method: method.toUpperCase(),
url: response.config.url || 'Unknown URL',
status: response.status,
},
response.data,
);
}
}
14 changes: 10 additions & 4 deletions src/api/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,25 @@ import qs from 'qs';
import { api } from './api';
import { DeployResponse, HttpResponse } from './types';

export async function getDeploy(id: string): Promise<DeployResponse> {
export async function getDeploy({ orgCode, id }: { orgCode: string; id: string }): Promise<DeployResponse> {
const { body } = await api<HttpResponse<DeployResponse>>({
method: 'get',
path: `/deploys/${id}`,
path: `/orgs/${orgCode}/deploys/${id}`,
});

return body.payload;
}

export async function getDeploys(query: { versionId: number }): Promise<DeployResponse[]> {
export async function getDeploys({
orgCode,
query,
}: {
orgCode: string;
query: { versionId: number };
}): Promise<DeployResponse[]> {
const { body } = await api<HttpResponse<DeployResponse[]>>({
method: 'get',
path: `/deploys/?${qs.stringify(query)}`,
path: `/orgs/${orgCode}/deploys/?${qs.stringify(query)}`,
});

return body.payload;
Expand Down
25 changes: 22 additions & 3 deletions src/api/profile.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { api, ApiError } from './api';
import { HttpResponse } from './types';
import { HttpResponse, SquidResponse } from './types';

export type Profile = {
username?: string;
Expand All @@ -18,11 +18,18 @@ export async function profile({
const { body } = await api<HttpResponse<Profile>>({
method: 'get',
auth,
path: `/profile`,
path: `/user`,
});

if (!body.payload) {
throw new ApiError(401, { error: 'username is missing' });
throw new ApiError(
{
status: 401,
method: 'get',
url: '/user',
},
{ error: 'Credentials are missing or invalid' },
);
}

return body.payload;
Expand All @@ -33,3 +40,15 @@ export async function listOrganizations() {

return organizations || [];
}

export async function listSquids({ squidName }: { squidName?: string }) {
const { body } = await api<HttpResponse<SquidResponse[]>>({
method: 'get',
path: `/user/squids`,
query: {
name: squidName,
},
});

return body.payload;
}
27 changes: 7 additions & 20 deletions src/api/secrets.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,19 @@
import { api } from './api';
import { HttpResponse, SecretsListResponse } from './types';

export async function listSecrets({ organization }: { organization?: string }): Promise<SecretsListResponse> {
export async function listSecrets({ orgCode }: { orgCode: string }): Promise<SecretsListResponse> {
const { body } = await api<HttpResponse<SecretsListResponse>>({
method: 'get',
query: organization
? {
organization,
}
: undefined,
path: `/secrets`,
path: `/orgs/${orgCode}/secrets`,
});
return body.payload;
}

export async function removeSecret({
name,
organization,
}: {
name: string;
organization?: string;
}): Promise<SecretsListResponse> {
export async function removeSecret({ name, orgCode }: { name: string; orgCode: string }): Promise<SecretsListResponse> {
const { body } = await api<HttpResponse<SecretsListResponse>>({
method: 'put',
path: `/secrets`,
path: `/orgs/${orgCode}/secrets`,
data: {
organization,
secrets: [{ action: 'DELETE', name }],
},
});
Expand All @@ -36,17 +24,16 @@ export async function removeSecret({
export async function setSecret({
name,
value,
organization,
orgCode,
}: {
name: string;
value: string;
organization?: string;
orgCode: string;
}): Promise<SecretsListResponse> {
const { body } = await api<HttpResponse<SecretsListResponse>>({
method: 'put',
path: `/secrets`,
path: `/orgs/${orgCode}/secrets`,
data: {
organization,
secrets: [{ action: 'UPDATE', name, value }],
},
});
Expand Down
Loading

0 comments on commit 9618f98

Please sign in to comment.