diff --git a/packages/clients/src/api/index.ts b/packages/clients/src/api/index.ts index acba37f20..d5e592435 100644 --- a/packages/clients/src/api/index.ts +++ b/packages/clients/src/api/index.ts @@ -10,6 +10,7 @@ export * as Function from './function' export * as IAM from './iam' export * as Instance from './instance' export * as IOT from './iot' +export * as IPFS from './ipfs' export * as K8S from './k8s' export * as LB from './lb' export * as Marketplace from './marketplace' diff --git a/packages/clients/src/api/ipfs/index.ts b/packages/clients/src/api/ipfs/index.ts new file mode 100644 index 000000000..e49bded05 --- /dev/null +++ b/packages/clients/src/api/ipfs/index.ts @@ -0,0 +1 @@ +export * as v1alpha1 from './v1alpha1/index.gen' diff --git a/packages/clients/src/api/ipfs/v1alpha1/api.gen.ts b/packages/clients/src/api/ipfs/v1alpha1/api.gen.ts new file mode 100644 index 000000000..e8b9e75d2 --- /dev/null +++ b/packages/clients/src/api/ipfs/v1alpha1/api.gen.ts @@ -0,0 +1,325 @@ +// This file was automatically generated. DO NOT EDIT. +// If you have any remark or suggestion do not hesitate to open an issue. +import { + API as ParentAPI, + enrichForPagination, + urlParams, + validatePathParam, + waitForResource, +} from '../../../bridge' +import type { Region, WaitForOptions } from '../../../bridge' +import { PIN_TRANSIENT_STATUSES } from './content.gen' +import { + marshalCreatePinByCIDRequest, + marshalCreatePinByURLRequest, + marshalCreateVolumeRequest, + marshalReplacePinRequest, + marshalUpdateVolumeRequest, + unmarshalListPinsResponse, + unmarshalListVolumesResponse, + unmarshalPin, + unmarshalReplacePinResponse, + unmarshalVolume, +} from './marshalling.gen' +import type { + CreatePinByCIDRequest, + CreatePinByURLRequest, + CreateVolumeRequest, + DeletePinRequest, + DeleteVolumeRequest, + GetPinRequest, + GetVolumeRequest, + ListPinsRequest, + ListPinsResponse, + ListVolumesRequest, + ListVolumesResponse, + Pin, + ReplacePinRequest, + ReplacePinResponse, + UpdateVolumeRequest, + Volume, +} from './types.gen' + +const jsonContentHeaders = { + 'Content-Type': 'application/json; charset=utf-8', +} + +/** IPFS Pinning service API. */ +export class API extends ParentAPI { + /** Lists the available regions of the API. */ + public static readonly LOCALITIES: Region[] = ['fr-par', 'nl-ams', 'pl-waw'] + + /** + * Create a new volume from a Project ID. Volume is identified by an ID and + * used to host pin references. Volume is personal (at least to your + * organization) even if IPFS blocks and CID are available to anyone. Should + * be the first command you made because every pin must be attached to a + * volume. + * + * @param request - The request {@link CreateVolumeRequest} + * @returns A Promise of Volume + */ + createVolume = (request: Readonly) => + this.client.fetch( + { + body: JSON.stringify( + marshalCreateVolumeRequest(request, this.client.settings), + ), + headers: jsonContentHeaders, + method: 'POST', + path: `/ipfs/v1alpha1/regions/${validatePathParam( + 'region', + request.region ?? this.client.settings.defaultRegion, + )}/volumes`, + }, + unmarshalVolume, + ) + + /** + * Retrieve information about a specific volume. + * + * @param request - The request {@link GetVolumeRequest} + * @returns A Promise of Volume + */ + getVolume = (request: Readonly) => + this.client.fetch( + { + method: 'GET', + path: `/ipfs/v1alpha1/regions/${validatePathParam( + 'region', + request.region ?? this.client.settings.defaultRegion, + )}/volumes/${validatePathParam('volumeId', request.volumeId)}`, + }, + unmarshalVolume, + ) + + protected pageOfListVolumes = (request: Readonly = {}) => + this.client.fetch( + { + method: 'GET', + path: `/ipfs/v1alpha1/regions/${validatePathParam( + 'region', + request.region ?? this.client.settings.defaultRegion, + )}/volumes`, + urlParams: urlParams( + ['order_by', request.orderBy ?? 'created_at_asc'], + ['page', request.page], + [ + 'page_size', + request.pageSize ?? this.client.settings.defaultPageSize, + ], + [ + 'project_id', + request.projectId ?? this.client.settings.defaultProjectId, + ], + ), + }, + unmarshalListVolumesResponse, + ) + + /** + * Retrieve information about all volumes from a Project ID. + * + * @param request - The request {@link ListVolumesRequest} + * @returns A Promise of ListVolumesResponse + */ + listVolumes = (request: Readonly = {}) => + enrichForPagination('volumes', this.pageOfListVolumes, request) + + /** + * Update volume information (tag, name...). + * + * @param request - The request {@link UpdateVolumeRequest} + * @returns A Promise of Volume + */ + updateVolume = (request: Readonly) => + this.client.fetch( + { + body: JSON.stringify( + marshalUpdateVolumeRequest(request, this.client.settings), + ), + headers: jsonContentHeaders, + method: 'PATCH', + path: `/ipfs/v1alpha1/regions/${validatePathParam( + 'region', + request.region ?? this.client.settings.defaultRegion, + )}/volumes/${validatePathParam('volumeId', request.volumeId)}`, + }, + unmarshalVolume, + ) + + /** + * Delete a volume by its ID and every pin attached to this volume. Can take a + * while, depending of your pinned content. + * + * @param request - The request {@link DeleteVolumeRequest} + */ + deleteVolume = (request: Readonly) => + this.client.fetch({ + method: 'DELETE', + path: `/ipfs/v1alpha1/regions/${validatePathParam( + 'region', + request.region ?? this.client.settings.defaultRegion, + )}/volumes/${validatePathParam('volumeId', request.volumeId)}`, + }) + + /** + * Create a pin request. Will fetch and store the content pointed by the + * provided URL. The content must be available on the public IPFS network. The + * content (IPFS blocks) will be host by the pinning service until pin + * deletion. From that point, any other IPFS peer can fetch and host your + * content: Make sure to pin public or encrypted content. Many pin requests + * (from different users) can target the same CID. A pin is defined by its ID + * (UUID), its status (queued, pinning, pinned or failed) and target CID. + * + * @param request - The request {@link CreatePinByURLRequest} + * @returns A Promise of Pin + */ + createPinByURL = (request: Readonly) => + this.client.fetch( + { + body: JSON.stringify( + marshalCreatePinByURLRequest(request, this.client.settings), + ), + headers: jsonContentHeaders, + method: 'POST', + path: `/ipfs/v1alpha1/regions/${validatePathParam( + 'region', + request.region ?? this.client.settings.defaultRegion, + )}/pins/create-by-url`, + }, + unmarshalPin, + ) + + /** + * Create a pin request. Will fetch and store the content pointed by the + * provided CID. The content must be available on the public IPFS network. The + * content (IPFS blocks) will be host by the pinning service until pin + * deletion. From that point, any other IPFS peer can fetch and host your + * content: Make sure to pin public or encrypted content. Many pin requests + * (from different users) can target the same CID. A pin is defined by its ID + * (UUID), its status (queued, pinning, pinned or failed) and target CID. + * + * @param request - The request {@link CreatePinByCIDRequest} + * @returns A Promise of Pin + */ + createPinByCID = (request: Readonly) => + this.client.fetch( + { + body: JSON.stringify( + marshalCreatePinByCIDRequest(request, this.client.settings), + ), + headers: jsonContentHeaders, + method: 'POST', + path: `/ipfs/v1alpha1/regions/${validatePathParam( + 'region', + request.region ?? this.client.settings.defaultRegion, + )}/pins/create-by-cid`, + }, + unmarshalPin, + ) + + replacePin = (request: Readonly) => + this.client.fetch( + { + body: JSON.stringify( + marshalReplacePinRequest(request, this.client.settings), + ), + headers: jsonContentHeaders, + method: 'POST', + path: `/ipfs/v1alpha1/regions/${validatePathParam( + 'region', + request.region ?? this.client.settings.defaultRegion, + )}/pins/${validatePathParam('pinId', request.pinId)}/replace`, + }, + unmarshalReplacePinResponse, + ) + + /** + * Retrieve information about the provided pin ID (not the CID): status, last + * modification, CID. + * + * @param request - The request {@link GetPinRequest} + * @returns A Promise of Pin + */ + getPin = (request: Readonly) => + this.client.fetch( + { + method: 'GET', + path: `/ipfs/v1alpha1/regions/${validatePathParam( + 'region', + request.region ?? this.client.settings.defaultRegion, + )}/pins/${validatePathParam('pinId', request.pinId)}`, + urlParams: urlParams(['volume_id', request.volumeId]), + }, + unmarshalPin, + ) + + /** + * Waits for {@link Pin} to be in a final state. + * + * @param request - The request {@link GetPinRequest} + * @param options - The waiting options + * @returns A Promise of Pin + */ + waitForPin = ( + request: Readonly, + options?: Readonly>, + ) => + waitForResource( + options?.stop ?? + (res => Promise.resolve(!PIN_TRANSIENT_STATUSES.includes(res.status))), + this.getPin, + request, + options, + ) + + protected pageOfListPins = (request: Readonly) => + this.client.fetch( + { + method: 'GET', + path: `/ipfs/v1alpha1/regions/${validatePathParam( + 'region', + request.region ?? this.client.settings.defaultRegion, + )}/pins`, + urlParams: urlParams( + ['order_by', request.orderBy ?? 'created_at_asc'], + ['organization_id', request.organizationId], + ['page', request.page], + [ + 'page_size', + request.pageSize ?? this.client.settings.defaultPageSize, + ], + ['project_id', request.projectId], + ['status', request.status ?? 'unknown_status'], + ['volume_id', request.volumeId], + ), + }, + unmarshalListPinsResponse, + ) + + /** + * Retrieve information about all pins into a volume. + * + * @param request - The request {@link ListPinsRequest} + * @returns A Promise of ListPinsResponse + */ + listPins = (request: Readonly) => + enrichForPagination('pins', this.pageOfListPins, request) + + /** + * Create an unpin request. If the pin was the last to target a specific CID, + * the content will be erase from storage. The function is indempotent. + * + * @param request - The request {@link DeletePinRequest} + */ + deletePin = (request: Readonly) => + this.client.fetch({ + method: 'DELETE', + path: `/ipfs/v1alpha1/regions/${validatePathParam( + 'region', + request.region ?? this.client.settings.defaultRegion, + )}/pins/${validatePathParam('pinId', request.pinId)}`, + urlParams: urlParams(['volume_id', request.volumeId]), + }) +} diff --git a/packages/clients/src/api/ipfs/v1alpha1/content.gen.ts b/packages/clients/src/api/ipfs/v1alpha1/content.gen.ts new file mode 100644 index 000000000..0e003479e --- /dev/null +++ b/packages/clients/src/api/ipfs/v1alpha1/content.gen.ts @@ -0,0 +1,6 @@ +// This file was automatically generated. DO NOT EDIT. +// If you have any remark or suggestion do not hesitate to open an issue. +import type { PinStatus } from './types.gen' + +/** Lists transient statutes of the enum {@link PinStatus}. */ +export const PIN_TRANSIENT_STATUSES: PinStatus[] = ['queued', 'pinning'] diff --git a/packages/clients/src/api/ipfs/v1alpha1/index.gen.ts b/packages/clients/src/api/ipfs/v1alpha1/index.gen.ts new file mode 100644 index 000000000..11566448a --- /dev/null +++ b/packages/clients/src/api/ipfs/v1alpha1/index.gen.ts @@ -0,0 +1,29 @@ +// This file was automatically generated. DO NOT EDIT. +// If you have any remark or suggestion do not hesitate to open an issue. +export { API } from './api.gen' +export * from './content.gen' +export type { + CreatePinByCIDRequest, + CreatePinByURLRequest, + CreateVolumeRequest, + DeletePinRequest, + DeleteVolumeRequest, + GetPinRequest, + GetVolumeRequest, + ListPinsRequest, + ListPinsRequestOrderBy, + ListPinsResponse, + ListVolumesRequest, + ListVolumesRequestOrderBy, + ListVolumesResponse, + Pin, + PinCID, + PinCIDMeta, + PinInfo, + PinOptions, + PinStatus, + ReplacePinRequest, + ReplacePinResponse, + UpdateVolumeRequest, + Volume, +} from './types.gen' diff --git a/packages/clients/src/api/ipfs/v1alpha1/marshalling.gen.ts b/packages/clients/src/api/ipfs/v1alpha1/marshalling.gen.ts new file mode 100644 index 000000000..dc9139468 --- /dev/null +++ b/packages/clients/src/api/ipfs/v1alpha1/marshalling.gen.ts @@ -0,0 +1,205 @@ +// This file was automatically generated. DO NOT EDIT. +// If you have any remark or suggestion do not hesitate to open an issue. +import { + isJSONObject, + unmarshalArrayOfObject, + unmarshalDate, +} from '../../../bridge' +import type { DefaultValues } from '../../../bridge' +import type { + CreatePinByCIDRequest, + CreatePinByURLRequest, + CreateVolumeRequest, + ListPinsResponse, + ListVolumesResponse, + Pin, + PinCID, + PinCIDMeta, + PinInfo, + PinOptions, + ReplacePinRequest, + ReplacePinResponse, + UpdateVolumeRequest, + Volume, +} from './types.gen' + +const unmarshalPinCIDMeta = (data: unknown) => { + if (!isJSONObject(data)) { + throw new TypeError( + `Unmarshalling the type 'PinCIDMeta' failed as data isn't a dictionary.`, + ) + } + + return { appId: data.app_id, url: data.url } as PinCIDMeta +} + +const unmarshalPinCID = (data: unknown) => { + if (!isJSONObject(data)) { + throw new TypeError( + `Unmarshalling the type 'PinCID' failed as data isn't a dictionary.`, + ) + } + + return { + cid: data.cid, + meta: data.meta ? unmarshalPinCIDMeta(data.meta) : undefined, + name: data.name, + origins: data.origins, + } as PinCID +} + +const unmarshalPinInfo = (data: unknown) => { + if (!isJSONObject(data)) { + throw new TypeError( + `Unmarshalling the type 'PinInfo' failed as data isn't a dictionary.`, + ) + } + + return { statusDetails: data.status_details } as PinInfo +} + +export const unmarshalPin = (data: unknown) => { + if (!isJSONObject(data)) { + throw new TypeError( + `Unmarshalling the type 'Pin' failed as data isn't a dictionary.`, + ) + } + + return { + cid: data.cid ? unmarshalPinCID(data.cid) : undefined, + createdAt: unmarshalDate(data.created_at), + delegates: data.delegates, + info: data.info ? unmarshalPinInfo(data.info) : undefined, + pinId: data.pin_id, + status: data.status, + } as Pin +} + +export const unmarshalVolume = (data: unknown) => { + if (!isJSONObject(data)) { + throw new TypeError( + `Unmarshalling the type 'Volume' failed as data isn't a dictionary.`, + ) + } + + return { + countPin: data.count_pin, + createdAt: unmarshalDate(data.created_at), + id: data.id, + name: data.name, + projectId: data.project_id, + region: data.region, + tags: data.tags, + updatedAt: unmarshalDate(data.updated_at), + } as Volume +} + +export const unmarshalListPinsResponse = (data: unknown) => { + if (!isJSONObject(data)) { + throw new TypeError( + `Unmarshalling the type 'ListPinsResponse' failed as data isn't a dictionary.`, + ) + } + + return { + pins: unmarshalArrayOfObject(data.pins, unmarshalPin), + totalCount: data.total_count, + } as ListPinsResponse +} + +export const unmarshalListVolumesResponse = (data: unknown) => { + if (!isJSONObject(data)) { + throw new TypeError( + `Unmarshalling the type 'ListVolumesResponse' failed as data isn't a dictionary.`, + ) + } + + return { + totalCount: data.total_count, + volumes: unmarshalArrayOfObject(data.volumes, unmarshalVolume), + } as ListVolumesResponse +} + +export const unmarshalReplacePinResponse = (data: unknown) => { + if (!isJSONObject(data)) { + throw new TypeError( + `Unmarshalling the type 'ReplacePinResponse' failed as data isn't a dictionary.`, + ) + } + + return { + pin: data.pin ? unmarshalPin(data.pin) : undefined, + } as ReplacePinResponse +} + +const marshalPinCIDMeta = ( + request: PinCIDMeta, + defaults: DefaultValues, +): Record => ({ + app_id: request.appId, + url: request.url, +}) + +const marshalPinOptions = ( + request: PinOptions, + defaults: DefaultValues, +): Record => ({ + replication_count: request.replicationCount, + required_zones: request.requiredZones, +}) + +export const marshalCreatePinByCIDRequest = ( + request: CreatePinByCIDRequest, + defaults: DefaultValues, +): Record => ({ + cid: request.cid, + meta: request.meta ? marshalPinCIDMeta(request.meta, defaults) : undefined, + name: request.name, + origins: request.origins, + pin_options: request.pinOptions + ? marshalPinOptions(request.pinOptions, defaults) + : undefined, + volume_id: request.volumeId, +}) + +export const marshalCreatePinByURLRequest = ( + request: CreatePinByURLRequest, + defaults: DefaultValues, +): Record => ({ + name: request.name, + pin_options: request.pinOptions + ? marshalPinOptions(request.pinOptions, defaults) + : undefined, + url: request.url, + volume_id: request.volumeId, +}) + +export const marshalCreateVolumeRequest = ( + request: CreateVolumeRequest, + defaults: DefaultValues, +): Record => ({ + name: request.name, + project_id: request.projectId ?? defaults.defaultProjectId, +}) + +export const marshalReplacePinRequest = ( + request: ReplacePinRequest, + defaults: DefaultValues, +): Record => ({ + cid: request.cid, + meta: request.meta ? marshalPinCIDMeta(request.meta, defaults) : undefined, + name: request.name, + origins: request.origins, + pin_options: request.pinOptions + ? marshalPinOptions(request.pinOptions, defaults) + : undefined, + volume_id: request.volumeId, +}) + +export const marshalUpdateVolumeRequest = ( + request: UpdateVolumeRequest, + defaults: DefaultValues, +): Record => ({ + name: request.name, + tags: request.tags, +}) diff --git a/packages/clients/src/api/ipfs/v1alpha1/types.gen.ts b/packages/clients/src/api/ipfs/v1alpha1/types.gen.ts new file mode 100644 index 000000000..134dfe7e7 --- /dev/null +++ b/packages/clients/src/api/ipfs/v1alpha1/types.gen.ts @@ -0,0 +1,196 @@ +// This file was automatically generated. DO NOT EDIT. +// If you have any remark or suggestion do not hesitate to open an issue. +import type { Region } from '../../../bridge' + +export type ListPinsRequestOrderBy = 'created_at_asc' | 'created_at_desc' + +export type ListVolumesRequestOrderBy = 'created_at_asc' | 'created_at_desc' + +export type PinStatus = + | 'unknown_status' + | 'queued' + | 'pinning' + | 'failed' + | 'pinned' + +export interface ListPinsResponse { + totalCount: number + pins: Pin[] +} + +export interface ListVolumesResponse { + volumes: Volume[] + totalCount: number +} + +export interface Pin { + pinId: string + status: PinStatus + createdAt?: Date + cid?: PinCID + delegates: string[] + info?: PinInfo +} + +export interface PinCID { + cid?: string + name?: string + origins: string[] + meta?: PinCIDMeta +} + +export interface PinCIDMeta { + appId: string + url?: string +} + +export interface PinInfo { + statusDetails?: string +} + +export interface PinOptions { + requiredZones: string[] + replicationCount: number +} + +export interface ReplacePinResponse { + pin?: Pin +} + +export interface Volume { + id: string + projectId: string + region: Region + countPin: number + createdAt?: Date + updatedAt?: Date + tags: string[] + name: string +} + +export type CreateVolumeRequest = { + /** + * Region to target. If none is passed will use default region from the + * config. + */ + region?: Region + projectId?: string + name: string +} + +export type GetVolumeRequest = { + /** + * Region to target. If none is passed will use default region from the + * config. + */ + region?: Region + volumeId: string +} + +export type ListVolumesRequest = { + /** + * Region to target. If none is passed will use default region from the + * config. + */ + region?: Region + projectId?: string + page?: number + pageSize?: number + orderBy?: ListVolumesRequestOrderBy +} + +export type UpdateVolumeRequest = { + /** + * Region to target. If none is passed will use default region from the + * config. + */ + region?: Region + volumeId: string + name?: string + tags?: string[] +} + +export type DeleteVolumeRequest = { + /** + * Region to target. If none is passed will use default region from the + * config. + */ + region?: Region + volumeId: string +} + +export type CreatePinByURLRequest = { + /** + * Region to target. If none is passed will use default region from the + * config. + */ + region?: Region + volumeId: string + url: string + name?: string + pinOptions?: PinOptions +} + +export type CreatePinByCIDRequest = { + /** + * Region to target. If none is passed will use default region from the + * config. + */ + region?: Region + volumeId: string + cid: string + name?: string + origins?: string[] + meta?: PinCIDMeta + pinOptions?: PinOptions +} + +export type ReplacePinRequest = { + /** + * Region to target. If none is passed will use default region from the + * config. + */ + region?: Region + pinId: string + volumeId: string + cid: string + name?: string + origins?: string[] + meta?: PinCIDMeta + pinOptions?: PinOptions +} + +export type GetPinRequest = { + /** + * Region to target. If none is passed will use default region from the + * config. + */ + region?: Region + pinId: string + volumeId: string +} + +export type ListPinsRequest = { + /** + * Region to target. If none is passed will use default region from the + * config. + */ + region?: Region + volumeId: string + projectId?: string + organizationId?: string + page?: number + pageSize?: number + orderBy?: ListPinsRequestOrderBy + status?: PinStatus +} + +export type DeletePinRequest = { + /** + * Region to target. If none is passed will use default region from the + * config. + */ + region?: Region + pinId: string + volumeId: string +}