diff --git a/.manifest.json b/.manifest.json index 92964f9..87b1555 100644 --- a/.manifest.json +++ b/.manifest.json @@ -1,10 +1,10 @@ { - "liblabVersion": "2.1.31", - "date": "2024-09-06T14:42:21.534Z", + "liblabVersion": "2.3.1", + "date": "2024-09-11T23:34:32.865Z", "config": { "apiId": 1126, "sdkName": "salad-cloud-imds-sdk", - "sdkVersion": "0.9.0-alpha.1", + "sdkVersion": "0.9.0-alpha.2", "liblabVersion": "2", "deliveryMethods": ["zip"], "languages": ["typescript"], @@ -25,7 +25,7 @@ "homepage": "https://github.com/saladtechnologies/salad-cloud-imds-sdk-dotnet", "ignoreFiles": [".gitignore", "./LICENSE"], "liblabVersion": "2", - "sdkVersion": "0.9.0-alpha.1", + "sdkVersion": "0.9.0-alpha.2", "targetBranch": "main" }, "go": { @@ -33,7 +33,7 @@ "githubRepoName": "salad-cloud-imds-sdk-go", "ignoreFiles": [".gitignore", "./LICENSE"], "liblabVersion": "2", - "sdkVersion": "0.9.0-alpha.1", + "sdkVersion": "0.9.0-alpha.2", "targetBranch": "main" }, "java": { @@ -57,7 +57,7 @@ "homepage": "https://github.com/saladtechnologies/salad-cloud-imds-sdk-java", "ignoreFiles": [".gitignore", "./LICENSE"], "liblabVersion": "2", - "sdkVersion": "0.9.0-alpha.1", + "sdkVersion": "0.9.0-alpha.2", "targetBranch": "main" }, "python": { @@ -100,7 +100,7 @@ "githubRepoName": "salad-cloud-imds-sdk-python", "ignoreFiles": [".gitignore", "./LICENSE"], "liblabVersion": "2", - "sdkVersion": "0.9.0-alpha.1", + "sdkVersion": "0.9.0-alpha.2", "targetBranch": "main" }, "typescript": { @@ -123,7 +123,7 @@ "homepage": "https://github.com/saladtechnologies/salad-cloud-imds-sdk-javascript", "ignoreFiles": [".gitignore", "./LICENSE"], "liblabVersion": "2", - "sdkVersion": "0.9.0-alpha.1", + "sdkVersion": "0.9.0-alpha.2", "targetBranch": "main" } }, @@ -155,7 +155,7 @@ }, "multiTenant": true, "hooksLocation": { - "bucketKey": "7034/hooks.zip", + "bucketKey": "7096/hooks.zip", "bucketName": "prod-liblab-api-stack-hooks" }, "includeWatermark": false, @@ -189,7 +189,7 @@ "compilerOptions": { "target": "es6", "module": "Node16", - "lib": ["es2017", "dom"] + "lib": ["es2018", "dom"] }, "usesFormData": false, "authentication": {}, diff --git a/README.md b/README.md index 992460d..ccfbc30 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# SaladCloudImdsSdk TypeScript SDK 0.9.0-alpha.1 +# SaladCloudImdsSdk TypeScript SDK 0.9.0-alpha.2 Welcome to the SaladCloudImdsSdk SDK documentation. This guide will help you get started with integrating and using the SaladCloudImdsSdk SDK in your project. ## Versions - API version: `0.9.0-alpha.1` -- SDK version: `0.9.0-alpha.1` +- SDK version: `0.9.0-alpha.2` ## About the API diff --git a/documentation/models/ContainerStatus.md b/documentation/models/ContainerStatus.md index 4cdf0f8..44c3ee6 100644 --- a/documentation/models/ContainerStatus.md +++ b/documentation/models/ContainerStatus.md @@ -4,7 +4,7 @@ Represents the health statuses of the running container. **Properties** -| Name | Type | Required | Description | -| :------ | :------ | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| ready | boolean | ✅ | `true` if the running container is ready. If a readiness probe is defined, this returns the latest result of the probe. If a readiness probe is not defined but a startup probe is defined, this returns the same value as the `started` property. If neither a readiness probe nor a startup probe are defined, returns `true`. | -| started | boolean | ✅ | `true` if the running container is started. If a startup probe is defined, this returns the latest result of the probe. If a startup probe is not defined, returns `true`. | +| Name | Type | Required | Description | +| :------ | :-------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ready | `boolean` | ✅ | `true` if the running container is ready. If a readiness probe is defined, this returns the latest result of the probe. If a readiness probe is not defined but a startup probe is defined, this returns the same value as the `started` property. If neither a readiness probe nor a startup probe are defined, returns `true`. | +| started | `boolean` | ✅ | `true` if the running container is started. If a startup probe is defined, this returns the latest result of the probe. If a startup probe is not defined, returns `true`. | diff --git a/documentation/models/ContainerToken.md b/documentation/models/ContainerToken.md index 2f9c826..5341cdf 100644 --- a/documentation/models/ContainerToken.md +++ b/documentation/models/ContainerToken.md @@ -4,6 +4,6 @@ Represents the identity token of the running container. **Properties** -| Name | Type | Required | Description | -| :--- | :----- | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| jwt | string | ✅ | The JSON Web Token (JWT) that may be used to identify the running container. The JWT may be verified using the JSON Web Key Set (JWKS) available at https://matrix-rest-api.salad.com/.well-known/stash-jwks.json. | +| Name | Type | Required | Description | +| :--- | :------- | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| jwt | `string` | ✅ | The JSON Web Token (JWT) that may be used to identify the running container. The JWT may be verified using the JSON Web Key Set (JWKS) available at https://matrix-rest-api.salad.com/.well-known/stash-jwks.json. | diff --git a/documentation/models/ReallocateContainer.md b/documentation/models/ReallocateContainer.md index 00c9ce4..ab192ba 100644 --- a/documentation/models/ReallocateContainer.md +++ b/documentation/models/ReallocateContainer.md @@ -4,6 +4,6 @@ Represents a request to reallocate a container. **Properties** -| Name | Type | Required | Description | -| :----- | :----- | :------- | :---------------------------------------------------------------------------------------------------------------------------- | -| reason | string | ✅ | The reason for reallocating the container. This value is reported to SaladCloud support for quality assurance of Salad Nodes. | +| Name | Type | Required | Description | +| :----- | :------- | :------- | :---------------------------------------------------------------------------------------------------------------------------- | +| reason | `string` | ✅ | The reason for reallocating the container. This value is reported to SaladCloud support for quality assurance of Salad Nodes. | diff --git a/documentation/services/MetadataService.md b/documentation/services/MetadataService.md index 9dee682..435eae6 100644 --- a/documentation/services/MetadataService.md +++ b/documentation/services/MetadataService.md @@ -17,9 +17,9 @@ Reallocates the running container to another Salad Node **Parameters** -| Name | Type | Required | Description | -| :--- | :------------------------------------------------------ | :------- | :---------------- | -| body | [ReallocateContainer](../models/ReallocateContainer.md) | ✅ | The request body. | +| Name | Type | Required | Description | +| :--- | :-------------------------------------------------------- | :------- | :---------------- | +| body | `[ReallocateContainer](../models/ReallocateContainer.md)` | ✅ | The request body. | **Example Usage Code Snippet** @@ -30,7 +30,7 @@ import { ReallocateContainer, SaladCloudImdsSdk } from '@saladtechnologies-oss/s const saladCloudImdsSdk = new SaladCloudImdsSdk({}); const reallocateContainer: ReallocateContainer = { - reason: 'aliqua ad dolor officia', + reason: 'laborum culpa', }; const { data } = await saladCloudImdsSdk.metadata.reallocateContainer(input); diff --git a/documentation/snippets/v1-reallocate-post.md b/documentation/snippets/v1-reallocate-post.md index d305805..96cf647 100644 --- a/documentation/snippets/v1-reallocate-post.md +++ b/documentation/snippets/v1-reallocate-post.md @@ -5,7 +5,7 @@ import { ReallocateContainer, SaladCloudImdsSdk } from '@saladtechnologies-oss/s const saladCloudImdsSdk = new SaladCloudImdsSdk({}); const reallocateContainer: ReallocateContainer = { - reason: 'aliqua ad dolor officia', + reason: 'laborum culpa', }; const { data } = await saladCloudImdsSdk.metadata.reallocateContainer(input); diff --git a/examples/package.json b/examples/package.json index f33da87..4396009 100644 --- a/examples/package.json +++ b/examples/package.json @@ -1,6 +1,6 @@ { "name": "salad-cloud-imds-sdk", - "version": "0.9.0-alpha.1", + "version": "0.9.0-alpha.2", "private": true, "dependencies": { "@saladtechnologies-oss/salad-cloud-imds-sdk": "file:../", diff --git a/examples/tsconfig.json b/examples/tsconfig.json index 78eb691..7f034f1 100644 --- a/examples/tsconfig.json +++ b/examples/tsconfig.json @@ -15,7 +15,7 @@ "isolatedModules": true, "allowSyntheticDefaultImports": true, "declarationMap": true, - "lib": ["es2017", "dom"], + "lib": ["es2018", "dom"], "sourceMap": true }, "include": ["src/"] diff --git a/package-lock.json b/package-lock.json index 543864e..b780db6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@saladtechnologies-oss/salad-cloud-imds-sdk", - "version": "0.9.0-alpha.1", + "version": "0.9.0-alpha.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@saladtechnologies-oss/salad-cloud-imds-sdk", - "version": "0.9.0-alpha.1", + "version": "0.9.0-alpha.2", "license": "MIT", "dependencies": { "zod": "3.22.0" @@ -511,9 +511,9 @@ } }, "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 354d9b8..e0c0941 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "@saladtechnologies-oss/salad-cloud-imds-sdk", - "version": "0.9.0-alpha.1", + "version": "0.9.0-alpha.2", "description": "The SaladCloud Instance Metadata Service (IMDS). Please refer to the [SaladCloud API Documentation](https://docs.salad.com/api-reference) for more details.", "source": "./src/index.ts", - "main": "./dist/commonjs/index.js", - "module": "./dist/esm/index.js", - "browser": "./dist/index.umd.js", - "unpkg": "./dist/index.umd.js", - "types": "./dist/commonjs/index.d.ts", + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "browser": "./dist/index.js", + "unpkg": "./dist/index.js", + "types": "./dist/index.d.ts", "scripts": { "test": "tsc --noEmit", "build": "tsup-node src/index.ts --format cjs,esm --dts --clean", diff --git a/src/http/client.ts b/src/http/client.ts index 10c4bcd..0bd0221 100644 --- a/src/http/client.ts +++ b/src/http/client.ts @@ -27,6 +27,19 @@ export class HttpClient { return this.requestHandlerChain.callChain(request); } + public async callPaginated(request: Request): Promise> { + const response = await this.call(request as any); + + if (!response.data) { + throw new Error('now response data to paginate through'); + } + + return { + ...response, + data: this.getPage(request, response.data), + }; + } + setBaseUrl(url: string): void { this.config.baseUrl = url; } @@ -34,4 +47,23 @@ export class HttpClient { setConfig(config: SdkConfig): void { this.config = config; } + + private getPage(request: Request, data: FullResponse): Page { + if (!request.pagination) { + throw new Error('getPage called for request without pagination property'); + } + + let curr: any = data; + for (const segment of request.pagination?.pagePath || []) { + curr = curr[segment]; + } + + const page = request.pagination?.pageSchema?.parse(curr); + if (!page) { + throw new Error( + `error getting page data. Curr: ${JSON.stringify(curr)}. PagePath: ${request.pagination?.pagePath}. Data: ${JSON.stringify(data)}`, + ); + } + return page; + } } diff --git a/src/http/index.ts b/src/http/index.ts index 84ea249..82bf7c7 100644 --- a/src/http/index.ts +++ b/src/http/index.ts @@ -1 +1,9 @@ -export type { SdkConfig, RequestConfig, RetryOptions, ValidationOptions } from './types.ts'; +export type { + SdkConfig, + RequestConfig, + RetryOptions, + ValidationOptions, + HttpMetadata, + HttpMethod, + HttpResponse, +} from './types.ts'; diff --git a/src/http/transport/request-builder.ts b/src/http/transport/request-builder.ts index a860fdd..3d49a5a 100644 --- a/src/http/transport/request-builder.ts +++ b/src/http/transport/request-builder.ts @@ -1,11 +1,11 @@ import z, { ZodType } from 'zod'; -import { Request, CreateRequestParameters, RequestParameter } from './request'; +import { Request, CreateRequestParameters, RequestParameter, RequestPagination } from './request'; import { ContentType, HttpMethod, SdkConfig, RequestConfig, RetryOptions, ValidationOptions } from '../types'; import { Environment } from '../environment'; import { SerializationStyle } from '../serialization/base-serializer'; -export class RequestBuilder { - private params: CreateRequestParameters; +export class RequestBuilder { + private params: CreateRequestParameters; constructor() { this.params = { @@ -30,7 +30,7 @@ export class RequestBuilder { }; } - setRetryAttempts(sdkConfig?: SdkConfig, requestConfig?: RequestConfig): RequestBuilder { + setRetryAttempts(sdkConfig?: SdkConfig, requestConfig?: RequestConfig): RequestBuilder { if (requestConfig?.retry?.attempts !== undefined) { this.params.retry.attempts = requestConfig.retry.attempts; } else if (sdkConfig?.retry?.attempts !== undefined) { @@ -40,7 +40,7 @@ export class RequestBuilder { return this; } - setRetryDelayMs(sdkConfig?: SdkConfig, requestConfig?: RequestConfig): RequestBuilder { + setRetryDelayMs(sdkConfig?: SdkConfig, requestConfig?: RequestConfig): RequestBuilder { if (requestConfig?.retry?.delayMs !== undefined) { this.params.retry.delayMs = requestConfig.retry.delayMs; } else if (sdkConfig?.retry?.delayMs !== undefined) { @@ -50,7 +50,7 @@ export class RequestBuilder { return this; } - setResponseValidation(sdkConfig: SdkConfig, requestConfig?: RequestConfig): RequestBuilder { + setResponseValidation(sdkConfig: SdkConfig, requestConfig?: RequestConfig): RequestBuilder { if (requestConfig?.validation?.responseValidation !== undefined) { this.params.validation.responseValidation = requestConfig.validation.responseValidation; } else if (sdkConfig?.validation?.responseValidation !== undefined) { @@ -60,7 +60,7 @@ export class RequestBuilder { return this; } - setBaseUrl(sdkConfig: SdkConfig): RequestBuilder { + setBaseUrl(sdkConfig: SdkConfig): RequestBuilder { if (sdkConfig?.baseUrl !== undefined) { this.params.baseUrl = sdkConfig.baseUrl; } @@ -68,49 +68,54 @@ export class RequestBuilder { return this; } - setMethod(method: HttpMethod): RequestBuilder { + setMethod(method: HttpMethod): RequestBuilder { this.params.method = method; return this; } - setPath(path: string): RequestBuilder { + setPath(path: string): RequestBuilder { this.params.path = path; return this; } - setConfig(config: SdkConfig): RequestBuilder { + setConfig(config: SdkConfig): RequestBuilder { this.params.config = config; return this; } - setRequestContentType(contentType: ContentType): RequestBuilder { + setRequestContentType(contentType: ContentType): RequestBuilder { this.params.requestContentType = contentType; return this; } - setResponseContentType(contentType: ContentType): RequestBuilder { + setResponseContentType(contentType: ContentType): RequestBuilder { this.params.responseContentType = contentType; return this; } - setRequestSchema(requestSchema: ZodType): RequestBuilder { + setRequestSchema(requestSchema: ZodType): RequestBuilder { this.params.requestSchema = requestSchema; return this; } - setResponseSchema(responseSchema: ZodType): RequestBuilder { + setResponseSchema(responseSchema: ZodType): RequestBuilder { this.params.responseSchema = responseSchema; return this; } - addBody(body?: any): RequestBuilder { + setPagination(pagination: RequestPagination): RequestBuilder { + this.params.pagination = pagination; + return this; + } + + addBody(body?: any): RequestBuilder { if (body !== undefined) { this.params.body = body; } return this; } - addPathParam(param: Partial): RequestBuilder { + addPathParam(param: Partial): RequestBuilder { if (param.value === undefined || param.key === undefined) { return this; } @@ -121,12 +126,14 @@ export class RequestBuilder { explode: param.explode ?? true, style: param.style ?? SerializationStyle.SIMPLE, encode: param.encode ?? true, + isLimit: !!param.isLimit, + isOffset: !!param.isOffset, }); return this; } - addQueryParam(param: Partial): RequestBuilder { + addQueryParam(param: Partial): RequestBuilder { if (param.value === undefined || param.key === undefined) { return this; } @@ -137,12 +144,14 @@ export class RequestBuilder { explode: param.explode ?? true, style: param.style ?? SerializationStyle.FORM, encode: param.encode ?? true, + isLimit: !!param.isLimit, + isOffset: !!param.isOffset, }); return this; } - addHeaderParam(param: Partial): RequestBuilder { + addHeaderParam(param: Partial): RequestBuilder { if (param.value === undefined || param.key === undefined) { return this; } @@ -153,12 +162,14 @@ export class RequestBuilder { explode: param.explode ?? true, style: param.style ?? SerializationStyle.SIMPLE, encode: param.encode ?? false, + isLimit: !!param.isLimit, + isOffset: !!param.isOffset, }); return this; } - public build(): Request { - return new Request(this.params); + public build(): Request { + return new Request(this.params); } } diff --git a/src/http/transport/request.ts b/src/http/transport/request.ts index 92d5648..7d0e10b 100644 --- a/src/http/transport/request.ts +++ b/src/http/transport/request.ts @@ -6,7 +6,7 @@ import { HeaderSerializer } from '../serialization/header-serializer'; import { HttpRequest } from '../hooks/hook'; import { SerializationStyle } from '../serialization/base-serializer'; -export interface CreateRequestParameters { +export interface CreateRequestParameters { baseUrl: string; method: HttpMethod; body?: any; @@ -15,12 +15,13 @@ export interface CreateRequestParameters { pathParams: Map; path: string; config: SdkConfig; - responseSchema: ZodType; + responseSchema: ZodType; requestSchema: ZodType; requestContentType: ContentType; responseContentType: ContentType; validation: ValidationOptions; retry: RetryOptions; + pagination?: RequestPagination; } export interface RequestParameter { @@ -29,9 +30,17 @@ export interface RequestParameter { explode: boolean; encode: boolean; style: SerializationStyle; + isLimit: boolean; + isOffset: boolean; } -export class Request { +export interface RequestPagination { + pageSize: number; + pagePath: string[]; + pageSchema?: ZodType; +} + +export class Request { public baseUrl: string = ''; public headers: Map = new Map(); @@ -60,9 +69,11 @@ export class Request { public retry: RetryOptions = {} as any; + public pagination?: RequestPagination; + private readonly pathPattern: string; - constructor(params: CreateRequestParameters) { + constructor(params: CreateRequestParameters) { this.baseUrl = params.baseUrl; this.method = params.method; this.pathPattern = params.path; @@ -78,6 +89,7 @@ export class Request { this.responseContentType = params.responseContentType; this.retry = params.retry; this.validation = params.validation; + this.pagination = params.pagination; } addHeaderParam(key: string, param: RequestParameter): void { @@ -203,7 +215,43 @@ export class Request { return new HeaderSerializer().serialize(this.headers); } + public nextPage() { + if (!this.pagination) { + return; + } + + const offsetParam = this.getOffsetParam(); + if (!offsetParam) { + return; + } + + offsetParam.value = Number(offsetParam.value) + this.pagination.pageSize; + } + private constructPath(): string { return new PathSerializer().serialize(this.pathPattern, this.pathParams); } + + private getOffsetParam(): RequestParameter | undefined { + const offsetParam = this.getAllParams().find((param) => param.isOffset); + return offsetParam; + } + + private getAllParams(): RequestParameter[] { + const allParams: RequestParameter[] = []; + + this.headers.forEach((val, key) => { + allParams.push(val); + }); + + this.queryParams.forEach((val, key) => { + allParams.push(val); + }); + + this.pathParams.forEach((val, key) => { + allParams.push(val); + }); + + return allParams; + } } diff --git a/src/http/transport/transport-hook-adapter.ts b/src/http/transport/transport-hook-adapter.ts index 82f30d3..7634368 100644 --- a/src/http/transport/transport-hook-adapter.ts +++ b/src/http/transport/transport-hook-adapter.ts @@ -78,6 +78,8 @@ export class TransportHookAdapter { encode: requestParam?.encode ?? false, style: requestParam?.style || SerializationStyle.NONE, explode: requestParam?.explode ?? false, + isLimit: requestParam?.isLimit ?? false, + isOffset: requestParam?.isOffset ?? false, }); }); return transportParams; diff --git a/src/services/metadata/metadata.ts b/src/services/metadata/metadata.ts index 2efd6cc..abfa63f 100644 --- a/src/services/metadata/metadata.ts +++ b/src/services/metadata/metadata.ts @@ -17,8 +17,8 @@ export class MetadataService extends BaseService { requestConfig?: RequestConfig, ): Promise> { const request = new RequestBuilder() - .setConfig(this.config) .setBaseUrl(this.config) + .setConfig(this.config) .setMethod('POST') .setPath('/v1/reallocate') .setRequestSchema(reallocateContainerRequest) @@ -40,8 +40,8 @@ export class MetadataService extends BaseService { */ async getContainerStatus(requestConfig?: RequestConfig): Promise> { const request = new RequestBuilder() - .setConfig(this.config) .setBaseUrl(this.config) + .setConfig(this.config) .setMethod('GET') .setPath('/v1/status') .setRequestSchema(z.any()) @@ -61,8 +61,8 @@ export class MetadataService extends BaseService { */ async getContainerToken(requestConfig?: RequestConfig): Promise> { const request = new RequestBuilder() - .setConfig(this.config) .setBaseUrl(this.config) + .setConfig(this.config) .setMethod('GET') .setPath('/v1/token') .setRequestSchema(z.any()) diff --git a/tsconfig.json b/tsconfig.json index 78eb691..7f034f1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,7 @@ "isolatedModules": true, "allowSyntheticDefaultImports": true, "declarationMap": true, - "lib": ["es2017", "dom"], + "lib": ["es2018", "dom"], "sourceMap": true }, "include": ["src/"]