Compare commits

..

1 Commits

Author SHA1 Message Date
Dimitrios Vasilas eb72f28599 rename metricClass to metricsClass 2023-05-25 12:05:38 +02:00
7 changed files with 3989 additions and 213 deletions

2
.gitignore vendored
View File

@ -102,5 +102,5 @@ dist
# TernJS port file
.tern-port
lib/
lib/**
openapi.yaml

12
.swcrc
View File

@ -1,12 +0,0 @@
{
"$schema": "https://swc.rs/schema.json",
"jsc": {
"parser": {
"syntax": "typescript"
},
"target": "es2017"
},
"module": {
"type": "commonjs"
}
}

View File

@ -1,44 +0,0 @@
# Scuba Client Library
This repository provides a client library for the Scuba service.
The repository also provides a CLI binary to interact with the
Scuba Service.
The supported operations are:
- Get the utilization metrics at a given time
- Get the latest utilization metrics
- Check the health of the Scuba service
## Contributing
In order to contribute, please follow the
[Contributing Guidelines](
https://github.com/scality/Guidelines/blob/master/CONTRIBUTING.md).
## Prerequisite
- Recommended Node version: >16.x.
- Yarn must be installed to build the project.
- An Open API yaml file defining the routes to use.
Node.js can be installed from [nodejs.org](https://nodejs.org/en/download/) and
Yarn can be installed from [yarnpkg.com](https://yarnpkg.com/en/docs/install).
## Usage
To generate the client, run the following command:
```bash
./bin/generate-client.sh
```
### Authentication
ScubaClient supports AWS Signature Version 4 authentication. To use this
authentication method, you must have a set of credentials with permission
to perform the desired operations.
### Command-Line Interface
Command-line support is not yet available.

View File

@ -10,8 +10,9 @@
"build": "tsc --strict",
"lint": "prettier -c $(git ls-files '*.[jt]s') && eslint $(git ls-files '*.[jt]s')",
"format": "prettier -w src && yarn lint --fix",
"postprepare": "[ -d lib ] || swc -d lib --copy-files index.ts src",
"prepack": "tsc --strict"
"postinstall": "yarn build",
"prepack": "yarn build",
"prepare": "yarn build"
},
"devDependencies": {
"@babel/core": "^7.20.2",
@ -26,22 +27,15 @@
"eslint": "^8.28.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^8.8.0",
"eslint-config-scality": "git+https://git.yourcmc.ru/vitalif/zenko-eslint-config-scality.git",
"eslint-config-scality": "git+https://github.com/scality/Guidelines#7.10.2",
"eslint-plugin-import": "^2.27.5",
"prettier": "^2.8.7",
"typescript": "^4.9.5"
},
"dependencies": {
"@aws-crypto/sha256-js": "^5.2.0",
"@smithy/signature-v4": "^2.1.1",
"@swc/cli": "^0.4.0",
"@swc/core": "^1.7.4",
"axios": "^1.3.4"
},
"engines": {
"node": ">= 16"
},
"peerDependencies": {
"typescript": "^4.9.5"
}
}

View File

@ -142,38 +142,6 @@ export const ScubaApiAxiosParamCreator = function (configuration?: Configuration
};
localVarRequestOptions.data = serializeDataIfNeeded(body, localVarRequestOptions, configuration);
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
healthCheck: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = '/health/deep';
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options };
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
localVarHeaderParameter['Content-Type'] = 'application/json';
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {
...localVarHeaderParameter,
...headersFromBaseOptions,
};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
@ -236,16 +204,6 @@ export const ScubaApiFp = function (configuration?: Configuration) {
);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getHealthCheck(
options?: AxiosRequestConfig,
): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.healthCheck(options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
};
};
@ -335,16 +293,4 @@ export class ScubaApi extends BaseAPI {
.getMetrics(metricsClass, resourceName, metricsDate, body, options)
.then(request => request(this.axios, this.basePath));
}
/**
* Health check endpoint
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof ScubaApi
*/
public healthCheck(options?: AxiosRequestConfig) {
return ScubaApiFp(this.configuration)
.getHealthCheck(options)
.then(request => request(this.axios, this.basePath));
}
}

View File

@ -1,26 +1,12 @@
import * as http from 'http';
import * as https from 'https';
import { SignatureV4 } from '@smithy/signature-v4';
import globalAxios, { AxiosRequestConfig, AxiosInstance, AxiosHeaders } from 'axios';
import { Sha256 } from '@aws-crypto/sha256-js';
import { URL } from 'url';
import { parse as parseQuerystring } from 'querystring';
import { ScubaApi } from './api';
import { AxiosRequestConfig } from 'axios';
import { Configuration, ConfigurationParameters } from './configuration';
import { ScubaApi } from './api';
export type MetricsClass = 'account' | 'bucket' | 'service';
type WithOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
export type SignatureParameter = WithOptional<
ConstructorParameters<typeof SignatureV4>[0],
'service' | 'region' | 'sha256'
>;
export type ScubaAuth = {
awsV4?: SignatureParameter;
};
export type ScubaClientParameters = Omit<
ConfigurationParameters,
'username' | 'password' | 'accessToken' | 'formDataCtor'
@ -32,7 +18,6 @@ export type ScubaClientParameters = Omit<
cert?: string;
ca?: string;
keepAlive?: boolean;
auth?: ScubaAuth;
};
export type ScubaMetrics = {
@ -40,12 +25,6 @@ export type ScubaMetrics = {
bytesTotal: number;
metricsClass: string;
resourceName: string;
id?: number;
date?: string;
};
export type HealthCheckResponse = {
date?: string;
};
function lpad(num: number, digits: number) {
@ -57,13 +36,8 @@ export default class ScubaClient {
private _defaultReqOptions: { httpAgent: http.Agent; httpsAgent: https.Agent };
private _axios: AxiosInstance;
/** Id of axios interceptor */
private _authInterceptor: number | null = null;
constructor(params?: ScubaClientParameters) {
const { basePath, host, port, useHttps, key, cert, ca, keepAlive, auth } = params || {};
const { basePath, host, port, useHttps, key, cert, ca, keepAlive } = params || {};
const proto = useHttps ? 'https' : 'http';
const _host = host || 'localhost';
const _port = port || 8100;
@ -80,67 +54,16 @@ export default class ScubaClient {
}),
};
this._axios = globalAxios.create();
// If basePath is a FQDN then it overrides the baked in config from the spec
this._api = new ScubaApi(new Configuration({ ...params, basePath: connectionString }), undefined, this._axios);
if (auth) {
this.setAuth(auth);
}
}
/**
* Remove current auth axios interceptor and add a new one
* @param {ScubaAuth} auth Authentication method and options
* @param {Aws4InterceptorParameter} [auth.awsV4] - parameters passed to aws4-axios
* @return {undefined}
*/
setAuth(auth: ScubaAuth): void {
if (this._authInterceptor !== null && this._authInterceptor !== undefined) {
this._axios.interceptors.request.eject(this._authInterceptor);
this._authInterceptor = null;
}
if (auth.awsV4) {
const signer = new SignatureV4({
service: 's3',
sha256: Sha256,
region: 'us-east-1',
...auth.awsV4,
});
this._authInterceptor = this._axios.interceptors.request.use(async req => {
const { host, hostname, pathname, protocol, search } = new URL(this._axios.getUri(req));
// remove first char '?' from qs
const query = parseQuerystring(search?.substring(1)) as Record<string, string | string[]>;
const requestToSign = {
method: req.method?.toUpperCase()!,
headers: { host, ...(req.headers as Record<string, any>) },
body: req.data,
host,
hostname,
path: pathname,
protocol,
query,
};
const res = await signer.sign(requestToSign);
// eslint-disable-next-line no-param-reassign
req.headers = new AxiosHeaders(res.headers);
return req;
});
}
this._api = new ScubaApi(new Configuration({ ...params, basePath: connectionString }));
}
async getLatestMetrics(
metricsClass: MetricsClass,
resourceName: string,
options?: AxiosRequestConfig,
body?: any,
): Promise<ScubaMetrics> {
const resp = (await this._api.getLatestMetrics(metricsClass, resourceName, body, {
const resp = (await this._api.getLatestMetrics(metricsClass, resourceName, undefined, {
...this._defaultReqOptions,
...options,
})) as any;
@ -152,21 +75,15 @@ export default class ScubaClient {
resourceName: string,
date: Date,
options?: AxiosRequestConfig,
body?: any,
): Promise<ScubaMetrics> {
const year = lpad(date.getUTCFullYear(), 4);
const month = lpad(date.getUTCMonth() + 1, 2);
const day = lpad(date.getUTCDate(), 2);
const year = lpad(date.getFullYear(), 4);
const month = lpad(date.getMonth(), 2);
const day = lpad(date.getDate(), 2);
const dateString = `${year}-${month}-${day}`;
const resp = (await this._api.getMetrics(metricsClass, resourceName, dateString, body, {
const resp = (await this._api.getMetrics(metricsClass, resourceName, dateString, undefined, {
...this._defaultReqOptions,
...options,
})) as any;
return resp.data;
}
async healthCheck(options?: AxiosRequestConfig): Promise<HealthCheckResponse> {
const resp = (await this._api.healthCheck({ ...this._defaultReqOptions, ...options })) as any;
return resp.data;
}
}

3975
yarn.lock Normal file

File diff suppressed because it is too large Load Diff