diff --git a/.aegir.cjs b/.aegir.cjs index b8f6f6f..99ae346 100644 --- a/.aegir.cjs +++ b/.aegir.cjs @@ -1,10 +1,43 @@ +// import { MockServer } from './test/MockServer' + +// let mockServer = new MockServer() +require('ts-node').register({ + project: 'tsconfig.json', +}) + +const { MockServerController } = require('./test/MockServerController') + + /** @type {import('aegir').PartialOptions} */ module.exports = { + docs: { + publish: true, + entryPoint: './' + }, tsRepo: true, build: { config: { platform: 'node' }, bundlesizeMax: '44KB' + }, + test: { + cov: false, + async before () { + return { + env: { + MOCK_PINNING_SERVER_SECRET: 'ci', + }, + controller: new MockServerController(), + } + }, + /** + * + * @param {GlobalOptions & TestOptions} _ + * @param {MockServerController} controller + */ + async after (_, {controller}) { + await controller.shutdown() + } } } diff --git a/.envrc b/.envrc-copy similarity index 68% rename from .envrc rename to .envrc-copy index e472052..85be14f 100644 --- a/.envrc +++ b/.envrc-copy @@ -1,5 +1,5 @@ use asdf export PATH=$(npm bin):${PATH} -export MOCK_PINNING_SERVER_PORT=3000 export MOCK_PINNING_SERVER_SECRET=secret +export DEBUG=0 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..0c0a3b9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +generated/** linguist-generated diff --git a/.github/workflows/js-test-and-release.yml b/.github/workflows/js-test-and-release.yml index 93cf843..ed07863 100644 --- a/.github/workflows/js-test-and-release.yml +++ b/.github/workflows/js-test-and-release.yml @@ -16,9 +16,14 @@ jobs: - uses: actions/setup-node@v2 with: node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master - - run: npm run --if-present lint - - run: npm run --if-present dep-check + - name: Install dependencies + run: npm install + - name: Generate client files + run: npm run gen + - name: lint + run: npm run --if-present lint + - name: dependency check + run: npm run --if-present dep-check test-node: needs: check @@ -26,14 +31,17 @@ jobs: strategy: matrix: os: [windows-latest, ubuntu-latest, macos-latest] - node: [16] + node: ['lts/*'] fail-fast: true steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: node-version: ${{ matrix.node }} - - uses: ipfs/aegir/actions/cache-node-modules@master + - name: Install dependencies + run: npm install + - name: Generate client files + run: npm run gen - run: npm run --if-present test:node - uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b # v2.1.0 with: @@ -48,7 +56,10 @@ jobs: - uses: actions/setup-node@v2 with: node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master + - name: Install dependencies + run: npm install + - name: Generate client files + run: npm run gen - run: npm run --if-present test:chrome - uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b # v2.1.0 with: @@ -63,7 +74,10 @@ jobs: - uses: actions/setup-node@v2 with: node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master + - name: Install dependencies + run: npm install + - name: Generate client files + run: npm run gen - run: npm run --if-present test:chrome-webworker - uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b # v2.1.0 with: @@ -78,7 +92,10 @@ jobs: - uses: actions/setup-node@v2 with: node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master + - name: Install dependencies + run: npm install + - name: Generate client files + run: npm run gen - run: npm run --if-present test:firefox - uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b # v2.1.0 with: @@ -93,7 +110,10 @@ jobs: - uses: actions/setup-node@v2 with: node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master + - name: Install dependencies + run: npm install + - name: Generate client files + run: npm run gen - run: npm run --if-present test:firefox-webworker - uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b # v2.1.0 with: @@ -108,7 +128,10 @@ jobs: - uses: actions/setup-node@v2 with: node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master + - name: Install dependencies + run: npm install + - name: Generate client files + run: npm run gen - run: npx xvfb-maybe npm run --if-present test:electron-main - uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b # v2.1.0 with: @@ -123,7 +146,10 @@ jobs: - uses: actions/setup-node@v2 with: node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master + - name: Install dependencies + run: npm install + - name: Generate client files + run: npm run gen - run: npx xvfb-maybe npm run --if-present test:electron-renderer - uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b # v2.1.0 with: @@ -141,7 +167,10 @@ jobs: - uses: actions/setup-node@v2 with: node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master + - name: Install dependencies + run: npm install + - name: Generate client files + run: npm run gen - uses: ipfs/aegir/actions/docker-login@master with: docker-token: ${{ secrets.DOCKER_TOKEN }} diff --git a/.github/workflows/test-and-release.yml b/.github/workflows/test-and-release.yml deleted file mode 100644 index 8468485..0000000 --- a/.github/workflows/test-and-release.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Test & Maybe Release -on: [push, pull_request] -jobs: - test: - strategy: - fail-fast: false - matrix: - node: [14.x, 16.x] - os: [macos-latest, ubuntu-latest, windows-latest] - runs-on: ${{ matrix.os }} - steps: - - name: Checkout Repository - uses: actions/checkout@v2.4.0 - - name: Use Node.js ${{ matrix.node }} - uses: actions/setup-node@v2.5.1 - with: - node-version: ${{ matrix.node }} - - name: Install Dependencies - run: | - npm install --no-progress - - name: Run tests - run: | - npm config set script-shell bash - npm run test:ci - release: - name: Release - needs: test - runs-on: ubuntu-latest - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - steps: - - name: Checkout - uses: actions/checkout@v2.4.0 - with: - fetch-depth: 0 - - name: Setup Node.js - uses: actions/setup-node@v2.5.1 - with: - node-version: 14 - - name: Install dependencies - run: | - npm install --no-progress --no-package-lock --no-save - - name: Build - run: | - npm run build - - name: Install plugins - run: | - npm install \ - @semantic-release/commit-analyzer \ - conventional-changelog-conventionalcommits \ - @semantic-release/release-notes-generator \ - @semantic-release/npm \ - @semantic-release/github \ - @semantic-release/git \ - @semantic-release/changelog \ - --no-progress --no-package-lock --no-save - - name: Release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - run: npx semantic-release diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 961e39f..e3f94a6 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -21,9 +21,16 @@ jobs: uses: actions/setup-node@v2.5.1 with: node-version: ${{ matrix.node-version }} + - name: Use Java for openapi generator + uses: actions/setup-java@v2 # https://github.com/actions/setup-java#basic + with: + distribution: 'temurin' + java-version: '17' - name: Install dependencies run: npm install + - name: Generate client from openapi spec + run: npm run gen - name: Typecheck uses: gozala/typescript-error-reporter-action@v1.0.8 with: - project: test/tsconfig.json + project: tsconfig.json diff --git a/.gitignore b/.gitignore index 8eb8cca..f24c437 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,9 @@ yarn.lock test/ts-use/tsconfig.tsbuildinfo test/tsconfig.tsbuildinfo package-lock.json +*.bak +.swc +.envrc +.nyc_output +generated/fetch/package.json +generated/fetch/tsconfig.json diff --git a/.mocharc.cjs b/.mocharc.cjs new file mode 100644 index 0000000..0094b57 --- /dev/null +++ b/.mocharc.cjs @@ -0,0 +1,4 @@ +module.exports = { + require: ['./fixtures.js'], + extensions: ['ts', 'tsx'], +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..cf6e0f9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,9 @@ +# Contributing guidelines + +IPFS as a project, including js-ipfs and all of its modules, follows the [standard IPFS Community contributing guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md). + +We also adhere to the [IPFS JavaScript Community contributing guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING_JS.md) which provide additional information of how to collaborate and contribute in the JavaScript implementation of IPFS. + +We appreciate your time and attention for going over these. Please open an issue on [ipfs/community](https://github.com/ipfs/community) if you have any question. + +Thank you. diff --git a/README.md b/README.md index 3146df3..dd2b440 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,45 @@ -## @ipfs-shipyard/pinning-service-client@1.0.0 +## IPFS Pinning Service API Client for JS -This generator creates TypeScript/JavaScript client that utilizes fetch-api. +This client was generated using [OpenAPI Generator](https://github.com/OpenAPITools/openapi-generator) from the [IPFS Pinning Service API spec](https://ipfs.github.io/pinning-services-api-spec/). + +You can see the commands used to generate the client in the `gen:fetch` npm script. + +### Usage + +``` +npm install @ipfs-shipyard/pinning-service-client --save +``` + +This client only has a programmatic API at the moment (no CLI). You use it like so: + +```ts + +import { Configuration, PinsApi, Status } from '@ipfs-shipyard/pinning-service-client' +import type { PinsGetRequest, PinResults } from '@ipfs-shipyard/pinning-service-client' + +const config = new Configuration({ + basePath, // the URI for your pinning provider, e.g. `http://localhost:3000` + accessToken, // the secret token/key given to you by your pinning provider + // fetchApi: fetch, // You can pass your own fetchApi implementation, but we use fetch-ponyfill by default. +}) + +const client = new PinsApi(config) + +(async () => { + // Get 10 failed Pins + const pinsGetOptions: PinsGetRequest = { + limit: 10, + status: new Set([Status.Failed]) // requires a set, and not an array + } + const {count, results}: PinResults = await client.pinsGet(pinsGetOptions) + + console.log(count, results) + +})() + +``` + +## Developing ### Building @@ -10,21 +49,21 @@ npm install npm run build ``` -### Publishing +### Contributing -First build the package then run ```npm publish``` +See [CONTRIBUTING.md](CONTRIBUTING.md). -### Consuming +### Publishing -navigate to the folder of your consuming project and run one of the following commands. +First build the package then run ```npm publish``` -_published:_ +## License -``` -npm install @ipfs-shipyard/pinning-service-client@0.0.0 --save -``` +Licensed under either of -_unPublished (not recommended):_ + * Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / http://www.apache.org/licenses/LICENSE-2.0) + * MIT ([LICENSE-MIT](LICENSE-MIT) / http://opensource.org/licenses/MIT) -``` -npm install PATH_TO_GENERATED_PACKAGE --save +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/fixtures.js b/fixtures.js new file mode 100644 index 0000000..2a2d4ec --- /dev/null +++ b/fixtures.js @@ -0,0 +1,9 @@ + +// @ts-check +// export async function mochaGlobalSetup () { + +// } + +// export async function mochaGlobalTeardown () { + +// } diff --git a/generated/fetch/.gitignore b/generated/fetch/.gitignore new file mode 100644 index 0000000..149b576 --- /dev/null +++ b/generated/fetch/.gitignore @@ -0,0 +1,4 @@ +wwwroot/*.js +node_modules +typings +dist diff --git a/generated/fetch/.npmignore b/generated/fetch/.npmignore new file mode 100644 index 0000000..42061c0 --- /dev/null +++ b/generated/fetch/.npmignore @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/generated/fetch/.openapi-generator-ignore b/generated/fetch/.openapi-generator-ignore new file mode 100644 index 0000000..7484ee5 --- /dev/null +++ b/generated/fetch/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/generated/fetch/.openapi-generator/FILES b/generated/fetch/.openapi-generator/FILES new file mode 100644 index 0000000..5e2cca3 --- /dev/null +++ b/generated/fetch/.openapi-generator/FILES @@ -0,0 +1,17 @@ +.gitignore +.npmignore +README.md +package.json +src/apis/PinsApi.ts +src/apis/index.ts +src/index.ts +src/models/Failure.ts +src/models/FailureError.ts +src/models/Pin.ts +src/models/PinResults.ts +src/models/PinStatus.ts +src/models/Status.ts +src/models/TextMatchingStrategy.ts +src/models/index.ts +src/runtime.ts +tsconfig.json diff --git a/generated/fetch/.openapi-generator/VERSION b/generated/fetch/.openapi-generator/VERSION new file mode 100644 index 0000000..1e20ec3 --- /dev/null +++ b/generated/fetch/.openapi-generator/VERSION @@ -0,0 +1 @@ +5.4.0 \ No newline at end of file diff --git a/generated/fetch/README.md b/generated/fetch/README.md new file mode 100644 index 0000000..a150abe --- /dev/null +++ b/generated/fetch/README.md @@ -0,0 +1,45 @@ +## @ipfs-shipyard/pinning-service-client@1.0.0 + +This generator creates TypeScript/JavaScript client that utilizes [Fetch API](https://fetch.spec.whatwg.org/). The generated Node module can be used in the following environments: + +Environment +* Node.js +* Webpack +* Browserify + +Language level +* ES5 - you must have a Promises/A+ library installed +* ES6 + +Module system +* CommonJS +* ES6 module system + +It can be used in both TypeScript and JavaScript. In TypeScript, the definition should be automatically resolved via `package.json`. ([Reference](http://www.typescriptlang.org/docs/handbook/typings-for-npm-packages.html)) + +### Building + +To build and compile the typescript sources to javascript use: +``` +npm install +npm run build +``` + +### Publishing + +First build the package then run ```npm publish``` + +### Consuming + +navigate to the folder of your consuming project and run one of the following commands. + +_published:_ + +``` +npm install @ipfs-shipyard/pinning-service-client@1.0.0 --save +``` + +_unPublished (not recommended):_ + +``` +npm install PATH_TO_GENERATED_PACKAGE --save diff --git a/generated/fetch/src/apis/PinsApi.ts b/generated/fetch/src/apis/PinsApi.ts new file mode 100644 index 0000000..f484e68 --- /dev/null +++ b/generated/fetch/src/apis/PinsApi.ts @@ -0,0 +1,405 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * IPFS Pinning Service API + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! 🏗️ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. # The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ## Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ## Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ## Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ## Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. # Provider hints A pinning service will use the DHT and other discovery methods to locate pinned content; however, it is a good practice to provide additional provider hints to speed up the discovery phase and start the transfer immediately, especially if a client has the data in their own datastore or already knows of other providers. The most common scenario is a client putting its own IPFS node\'s multiaddrs in `Pin.origins`, and then attempt to connect to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. At the same time, a pinning service will try to connect to multiaddrs provided by the client in `Pin.origins`. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and mutual direct dial between a client and a service works around peer routing issues in restrictive network topologies, such as NATs, firewalls, etc. **NOTE:** Connections to multiaddrs in `origins` and `delegates` arrays should be attempted in best-effort fashion, and dial failure should not fail the pinning operation. When unable to act on explicit provider hints, DHT and other discovery methods should be used as a fallback by a pinning service. **NOTE:** All multiaddrs MUST end with `/p2p/{peerID}` and SHOULD be fully resolved and confirmed to be dialable from the public internet. Avoid sending addresses from local networks. # Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ## Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ## Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import { + Failure, + FailureFromJSON, + FailureToJSON, + Pin, + PinFromJSON, + PinToJSON, + PinResults, + PinResultsFromJSON, + PinResultsToJSON, + PinStatus, + PinStatusFromJSON, + PinStatusToJSON, + Status, + StatusFromJSON, + StatusToJSON, + TextMatchingStrategy, + TextMatchingStrategyFromJSON, + TextMatchingStrategyToJSON, +} from '../models'; + +export interface PinsGetRequest { + cid?: Set; + name?: string; + match?: TextMatchingStrategy; + status?: Set; + before?: Date; + after?: Date; + limit?: number; + meta?: { [key: string]: string; }; +} + +export interface PinsPostRequest { + pin: Pin; +} + +export interface PinsRequestidDeleteRequest { + requestid: string; +} + +export interface PinsRequestidGetRequest { + requestid: string; +} + +export interface PinsRequestidPostRequest { + requestid: string; + pin: Pin; +} + +/** + * PinsApi - interface + * + * @export + * @interface PinsApiInterface + */ +export interface PinsApiInterface { + /** + * List all the pin objects, matching optional filters; when no filter is provided, only successful pins are returned + * @summary List pin objects + * @param {Set} [cid] Return pin objects responsible for pinning the specified CID(s); be aware that using longer hash functions introduces further constraints on the number of CIDs that will fit under the limit of 2000 characters per URL in browser contexts + * @param {string} [name] Return pin objects with specified name (by default a case-sensitive, exact match) + * @param {TextMatchingStrategy} [match] Customize the text matching strategy applied when name filter is present + * @param {Set} [status] Return pin objects for pins with the specified status + * @param {Date} [before] Return results created (queued) before provided timestamp + * @param {Date} [after] Return results created (queued) after provided timestamp + * @param {number} [limit] Max records to return + * @param {{ [key: string]: string; }} [meta] Return pin objects that match specified metadata + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof PinsApiInterface + */ + pinsGetRaw(requestParameters: PinsGetRequest, initOverrides?: RequestInit): Promise>; + + /** + * List all the pin objects, matching optional filters; when no filter is provided, only successful pins are returned + * List pin objects + */ + pinsGet(requestParameters: PinsGetRequest, initOverrides?: RequestInit): Promise; + + /** + * Add a new pin object for the current access token + * @summary Add pin object + * @param {Pin} pin + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof PinsApiInterface + */ + pinsPostRaw(requestParameters: PinsPostRequest, initOverrides?: RequestInit): Promise>; + + /** + * Add a new pin object for the current access token + * Add pin object + */ + pinsPost(requestParameters: PinsPostRequest, initOverrides?: RequestInit): Promise; + + /** + * Remove a pin object + * @summary Remove pin object + * @param {string} requestid + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof PinsApiInterface + */ + pinsRequestidDeleteRaw(requestParameters: PinsRequestidDeleteRequest, initOverrides?: RequestInit): Promise>; + + /** + * Remove a pin object + * Remove pin object + */ + pinsRequestidDelete(requestParameters: PinsRequestidDeleteRequest, initOverrides?: RequestInit): Promise; + + /** + * Get a pin object and its status + * @summary Get pin object + * @param {string} requestid + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof PinsApiInterface + */ + pinsRequestidGetRaw(requestParameters: PinsRequestidGetRequest, initOverrides?: RequestInit): Promise>; + + /** + * Get a pin object and its status + * Get pin object + */ + pinsRequestidGet(requestParameters: PinsRequestidGetRequest, initOverrides?: RequestInit): Promise; + + /** + * Replace an existing pin object (shortcut for executing remove and add operations in one step to avoid unnecessary garbage collection of blocks present in both recursive pins) + * @summary Replace pin object + * @param {string} requestid + * @param {Pin} pin + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof PinsApiInterface + */ + pinsRequestidPostRaw(requestParameters: PinsRequestidPostRequest, initOverrides?: RequestInit): Promise>; + + /** + * Replace an existing pin object (shortcut for executing remove and add operations in one step to avoid unnecessary garbage collection of blocks present in both recursive pins) + * Replace pin object + */ + pinsRequestidPost(requestParameters: PinsRequestidPostRequest, initOverrides?: RequestInit): Promise; + +} + +/** + * + */ +export class PinsApi extends runtime.BaseAPI implements PinsApiInterface { + + /** + * List all the pin objects, matching optional filters; when no filter is provided, only successful pins are returned + * List pin objects + */ + async pinsGetRaw(requestParameters: PinsGetRequest, initOverrides?: RequestInit): Promise> { + const queryParameters: any = {}; + + if (requestParameters.cid) { + queryParameters['cid'] = Array.from(requestParameters.cid).join(runtime.COLLECTION_FORMATS["csv"]); + } + + if (requestParameters.name !== undefined) { + queryParameters['name'] = requestParameters.name; + } + + if (requestParameters.match !== undefined) { + queryParameters['match'] = requestParameters.match; + } + + if (requestParameters.status) { + queryParameters['status'] = Array.from(requestParameters.status).join(runtime.COLLECTION_FORMATS["csv"]); + } + + if (requestParameters.before !== undefined) { + queryParameters['before'] = (requestParameters.before as any).toISOString(); + } + + if (requestParameters.after !== undefined) { + queryParameters['after'] = (requestParameters.after as any).toISOString(); + } + + if (requestParameters.limit !== undefined) { + queryParameters['limit'] = requestParameters.limit; + } + + if (requestParameters.meta !== undefined) { + queryParameters['meta'] = requestParameters.meta; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("accessToken", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + const response = await this.request({ + path: `/pins`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => PinResultsFromJSON(jsonValue)); + } + + /** + * List all the pin objects, matching optional filters; when no filter is provided, only successful pins are returned + * List pin objects + */ + async pinsGet(requestParameters: PinsGetRequest = {}, initOverrides?: RequestInit): Promise { + const response = await this.pinsGetRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Add a new pin object for the current access token + * Add pin object + */ + async pinsPostRaw(requestParameters: PinsPostRequest, initOverrides?: RequestInit): Promise> { + if (requestParameters.pin === null || requestParameters.pin === undefined) { + throw new runtime.RequiredError('pin','Required parameter requestParameters.pin was null or undefined when calling pinsPost.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("accessToken", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + const response = await this.request({ + path: `/pins`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: PinToJSON(requestParameters.pin), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => PinStatusFromJSON(jsonValue)); + } + + /** + * Add a new pin object for the current access token + * Add pin object + */ + async pinsPost(requestParameters: PinsPostRequest, initOverrides?: RequestInit): Promise { + const response = await this.pinsPostRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Remove a pin object + * Remove pin object + */ + async pinsRequestidDeleteRaw(requestParameters: PinsRequestidDeleteRequest, initOverrides?: RequestInit): Promise> { + if (requestParameters.requestid === null || requestParameters.requestid === undefined) { + throw new runtime.RequiredError('requestid','Required parameter requestParameters.requestid was null or undefined when calling pinsRequestidDelete.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("accessToken", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + const response = await this.request({ + path: `/pins/{requestid}`.replace(`{${"requestid"}}`, encodeURIComponent(String(requestParameters.requestid))), + method: 'DELETE', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * Remove a pin object + * Remove pin object + */ + async pinsRequestidDelete(requestParameters: PinsRequestidDeleteRequest, initOverrides?: RequestInit): Promise { + await this.pinsRequestidDeleteRaw(requestParameters, initOverrides); + } + + /** + * Get a pin object and its status + * Get pin object + */ + async pinsRequestidGetRaw(requestParameters: PinsRequestidGetRequest, initOverrides?: RequestInit): Promise> { + if (requestParameters.requestid === null || requestParameters.requestid === undefined) { + throw new runtime.RequiredError('requestid','Required parameter requestParameters.requestid was null or undefined when calling pinsRequestidGet.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("accessToken", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + const response = await this.request({ + path: `/pins/{requestid}`.replace(`{${"requestid"}}`, encodeURIComponent(String(requestParameters.requestid))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => PinStatusFromJSON(jsonValue)); + } + + /** + * Get a pin object and its status + * Get pin object + */ + async pinsRequestidGet(requestParameters: PinsRequestidGetRequest, initOverrides?: RequestInit): Promise { + const response = await this.pinsRequestidGetRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Replace an existing pin object (shortcut for executing remove and add operations in one step to avoid unnecessary garbage collection of blocks present in both recursive pins) + * Replace pin object + */ + async pinsRequestidPostRaw(requestParameters: PinsRequestidPostRequest, initOverrides?: RequestInit): Promise> { + if (requestParameters.requestid === null || requestParameters.requestid === undefined) { + throw new runtime.RequiredError('requestid','Required parameter requestParameters.requestid was null or undefined when calling pinsRequestidPost.'); + } + + if (requestParameters.pin === null || requestParameters.pin === undefined) { + throw new runtime.RequiredError('pin','Required parameter requestParameters.pin was null or undefined when calling pinsRequestidPost.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("accessToken", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + const response = await this.request({ + path: `/pins/{requestid}`.replace(`{${"requestid"}}`, encodeURIComponent(String(requestParameters.requestid))), + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: PinToJSON(requestParameters.pin), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => PinStatusFromJSON(jsonValue)); + } + + /** + * Replace an existing pin object (shortcut for executing remove and add operations in one step to avoid unnecessary garbage collection of blocks present in both recursive pins) + * Replace pin object + */ + async pinsRequestidPost(requestParameters: PinsRequestidPostRequest, initOverrides?: RequestInit): Promise { + const response = await this.pinsRequestidPostRaw(requestParameters, initOverrides); + return await response.value(); + } + +} diff --git a/generated/fetch/src/apis/index.ts b/generated/fetch/src/apis/index.ts new file mode 100644 index 0000000..9a1d85b --- /dev/null +++ b/generated/fetch/src/apis/index.ts @@ -0,0 +1,3 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './PinsApi'; diff --git a/generated/fetch/src/index.ts b/generated/fetch/src/index.ts new file mode 100644 index 0000000..be9d1ed --- /dev/null +++ b/generated/fetch/src/index.ts @@ -0,0 +1,5 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './runtime'; +export * from './apis'; +export * from './models'; diff --git a/generated/fetch/src/models/Failure.ts b/generated/fetch/src/models/Failure.ts new file mode 100644 index 0000000..aed6fcc --- /dev/null +++ b/generated/fetch/src/models/Failure.ts @@ -0,0 +1,63 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * IPFS Pinning Service API + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! 🏗️ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. # The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ## Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ## Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ## Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ## Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. # Provider hints A pinning service will use the DHT and other discovery methods to locate pinned content; however, it is a good practice to provide additional provider hints to speed up the discovery phase and start the transfer immediately, especially if a client has the data in their own datastore or already knows of other providers. The most common scenario is a client putting its own IPFS node\'s multiaddrs in `Pin.origins`, and then attempt to connect to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. At the same time, a pinning service will try to connect to multiaddrs provided by the client in `Pin.origins`. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and mutual direct dial between a client and a service works around peer routing issues in restrictive network topologies, such as NATs, firewalls, etc. **NOTE:** Connections to multiaddrs in `origins` and `delegates` arrays should be attempted in best-effort fashion, and dial failure should not fail the pinning operation. When unable to act on explicit provider hints, DHT and other discovery methods should be used as a fallback by a pinning service. **NOTE:** All multiaddrs MUST end with `/p2p/{peerID}` and SHOULD be fully resolved and confirmed to be dialable from the public internet. Avoid sending addresses from local networks. # Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ## Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ## Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import { + FailureError, + FailureErrorFromJSON, + FailureErrorFromJSONTyped, + FailureErrorToJSON, +} from './FailureError'; + +/** + * Response for a failed request + * @export + * @interface Failure + */ +export interface Failure { + /** + * + * @type {FailureError} + * @memberof Failure + */ + error: FailureError; +} + +export function FailureFromJSON(json: any): Failure { + return FailureFromJSONTyped(json, false); +} + +export function FailureFromJSONTyped(json: any, ignoreDiscriminator: boolean): Failure { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'error': FailureErrorFromJSON(json['error']), + }; +} + +export function FailureToJSON(value?: Failure | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'error': FailureErrorToJSON(value.error), + }; +} + diff --git a/generated/fetch/src/models/FailureError.ts b/generated/fetch/src/models/FailureError.ts new file mode 100644 index 0000000..78a6351 --- /dev/null +++ b/generated/fetch/src/models/FailureError.ts @@ -0,0 +1,64 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * IPFS Pinning Service API + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! 🏗️ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. # The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ## Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ## Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ## Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ## Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. # Provider hints A pinning service will use the DHT and other discovery methods to locate pinned content; however, it is a good practice to provide additional provider hints to speed up the discovery phase and start the transfer immediately, especially if a client has the data in their own datastore or already knows of other providers. The most common scenario is a client putting its own IPFS node\'s multiaddrs in `Pin.origins`, and then attempt to connect to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. At the same time, a pinning service will try to connect to multiaddrs provided by the client in `Pin.origins`. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and mutual direct dial between a client and a service works around peer routing issues in restrictive network topologies, such as NATs, firewalls, etc. **NOTE:** Connections to multiaddrs in `origins` and `delegates` arrays should be attempted in best-effort fashion, and dial failure should not fail the pinning operation. When unable to act on explicit provider hints, DHT and other discovery methods should be used as a fallback by a pinning service. **NOTE:** All multiaddrs MUST end with `/p2p/{peerID}` and SHOULD be fully resolved and confirmed to be dialable from the public internet. Avoid sending addresses from local networks. # Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ## Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ## Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface FailureError + */ +export interface FailureError { + /** + * Mandatory string identifying the type of error + * @type {string} + * @memberof FailureError + */ + reason: string; + /** + * Optional, longer description of the error; may include UUID of transaction for support, links to documentation etc + * @type {string} + * @memberof FailureError + */ + details?: string; +} + +export function FailureErrorFromJSON(json: any): FailureError { + return FailureErrorFromJSONTyped(json, false); +} + +export function FailureErrorFromJSONTyped(json: any, ignoreDiscriminator: boolean): FailureError { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'reason': json['reason'], + 'details': !exists(json, 'details') ? undefined : json['details'], + }; +} + +export function FailureErrorToJSON(value?: FailureError | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'reason': value.reason, + 'details': value.details, + }; +} + diff --git a/generated/fetch/src/models/Pin.ts b/generated/fetch/src/models/Pin.ts new file mode 100644 index 0000000..a0db4cb --- /dev/null +++ b/generated/fetch/src/models/Pin.ts @@ -0,0 +1,80 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * IPFS Pinning Service API + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! 🏗️ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. # The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ## Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ## Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ## Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ## Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. # Provider hints A pinning service will use the DHT and other discovery methods to locate pinned content; however, it is a good practice to provide additional provider hints to speed up the discovery phase and start the transfer immediately, especially if a client has the data in their own datastore or already knows of other providers. The most common scenario is a client putting its own IPFS node\'s multiaddrs in `Pin.origins`, and then attempt to connect to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. At the same time, a pinning service will try to connect to multiaddrs provided by the client in `Pin.origins`. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and mutual direct dial between a client and a service works around peer routing issues in restrictive network topologies, such as NATs, firewalls, etc. **NOTE:** Connections to multiaddrs in `origins` and `delegates` arrays should be attempted in best-effort fashion, and dial failure should not fail the pinning operation. When unable to act on explicit provider hints, DHT and other discovery methods should be used as a fallback by a pinning service. **NOTE:** All multiaddrs MUST end with `/p2p/{peerID}` and SHOULD be fully resolved and confirmed to be dialable from the public internet. Avoid sending addresses from local networks. # Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ## Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ## Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * Pin object + * @export + * @interface Pin + */ +export interface Pin { + /** + * Content Identifier (CID) to be pinned recursively + * @type {string} + * @memberof Pin + */ + cid: string; + /** + * Optional name for pinned data; can be used for lookups later + * @type {string} + * @memberof Pin + */ + name?: string; + /** + * Optional list of multiaddrs known to provide the data + * @type {Set} + * @memberof Pin + */ + origins?: Set; + /** + * Optional metadata for pin object + * @type {{ [key: string]: string; }} + * @memberof Pin + */ + meta?: { [key: string]: string; }; +} + +export function PinFromJSON(json: any): Pin { + return PinFromJSONTyped(json, false); +} + +export function PinFromJSONTyped(json: any, ignoreDiscriminator: boolean): Pin { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'cid': json['cid'], + 'name': !exists(json, 'name') ? undefined : json['name'], + 'origins': !exists(json, 'origins') ? undefined : json['origins'], + 'meta': !exists(json, 'meta') ? undefined : json['meta'], + }; +} + +export function PinToJSON(value?: Pin | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'cid': value.cid, + 'name': value.name, + 'origins': value.origins, + 'meta': value.meta, + }; +} + diff --git a/generated/fetch/src/models/PinResults.ts b/generated/fetch/src/models/PinResults.ts new file mode 100644 index 0000000..4ccf4fb --- /dev/null +++ b/generated/fetch/src/models/PinResults.ts @@ -0,0 +1,71 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * IPFS Pinning Service API + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! 🏗️ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. # The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ## Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ## Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ## Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ## Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. # Provider hints A pinning service will use the DHT and other discovery methods to locate pinned content; however, it is a good practice to provide additional provider hints to speed up the discovery phase and start the transfer immediately, especially if a client has the data in their own datastore or already knows of other providers. The most common scenario is a client putting its own IPFS node\'s multiaddrs in `Pin.origins`, and then attempt to connect to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. At the same time, a pinning service will try to connect to multiaddrs provided by the client in `Pin.origins`. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and mutual direct dial between a client and a service works around peer routing issues in restrictive network topologies, such as NATs, firewalls, etc. **NOTE:** Connections to multiaddrs in `origins` and `delegates` arrays should be attempted in best-effort fashion, and dial failure should not fail the pinning operation. When unable to act on explicit provider hints, DHT and other discovery methods should be used as a fallback by a pinning service. **NOTE:** All multiaddrs MUST end with `/p2p/{peerID}` and SHOULD be fully resolved and confirmed to be dialable from the public internet. Avoid sending addresses from local networks. # Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ## Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ## Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import { + PinStatus, + PinStatusFromJSON, + PinStatusFromJSONTyped, + PinStatusToJSON, +} from './PinStatus'; + +/** + * Response used for listing pin objects matching request + * @export + * @interface PinResults + */ +export interface PinResults { + /** + * The total number of pin objects that exist for passed query filters + * @type {number} + * @memberof PinResults + */ + count: number; + /** + * An array of PinStatus results + * @type {Set} + * @memberof PinResults + */ + results: Set; +} + +export function PinResultsFromJSON(json: any): PinResults { + return PinResultsFromJSONTyped(json, false); +} + +export function PinResultsFromJSONTyped(json: any, ignoreDiscriminator: boolean): PinResults { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'count': json['count'], + 'results': (new Set((json['results'] as Array).map(PinStatusFromJSON))), + }; +} + +export function PinResultsToJSON(value?: PinResults | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'count': value.count, + 'results': (Array.from(value.results as Set).map(PinStatusToJSON)), + }; +} + diff --git a/generated/fetch/src/models/PinStatus.ts b/generated/fetch/src/models/PinStatus.ts new file mode 100644 index 0000000..896ad3f --- /dev/null +++ b/generated/fetch/src/models/PinStatus.ts @@ -0,0 +1,109 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * IPFS Pinning Service API + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! 🏗️ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. # The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ## Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ## Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ## Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ## Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. # Provider hints A pinning service will use the DHT and other discovery methods to locate pinned content; however, it is a good practice to provide additional provider hints to speed up the discovery phase and start the transfer immediately, especially if a client has the data in their own datastore or already knows of other providers. The most common scenario is a client putting its own IPFS node\'s multiaddrs in `Pin.origins`, and then attempt to connect to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. At the same time, a pinning service will try to connect to multiaddrs provided by the client in `Pin.origins`. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and mutual direct dial between a client and a service works around peer routing issues in restrictive network topologies, such as NATs, firewalls, etc. **NOTE:** Connections to multiaddrs in `origins` and `delegates` arrays should be attempted in best-effort fashion, and dial failure should not fail the pinning operation. When unable to act on explicit provider hints, DHT and other discovery methods should be used as a fallback by a pinning service. **NOTE:** All multiaddrs MUST end with `/p2p/{peerID}` and SHOULD be fully resolved and confirmed to be dialable from the public internet. Avoid sending addresses from local networks. # Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ## Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ## Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import { + Pin, + PinFromJSON, + PinFromJSONTyped, + PinToJSON, +} from './Pin'; +import { + Status, + StatusFromJSON, + StatusFromJSONTyped, + StatusToJSON, +} from './Status'; + +/** + * Pin object with status + * @export + * @interface PinStatus + */ +export interface PinStatus { + /** + * Globally unique identifier of the pin request; can be used to check the status of ongoing pinning, or pin removal + * @type {string} + * @memberof PinStatus + */ + requestid: string; + /** + * + * @type {Status} + * @memberof PinStatus + */ + status: Status; + /** + * Immutable timestamp indicating when a pin request entered a pinning service; can be used for filtering results and pagination + * @type {Date} + * @memberof PinStatus + */ + created: Date; + /** + * + * @type {Pin} + * @memberof PinStatus + */ + pin: Pin; + /** + * List of multiaddrs designated by pinning service for transferring any new data from external peers + * @type {Set} + * @memberof PinStatus + */ + delegates: Set; + /** + * Optional info for PinStatus response + * @type {{ [key: string]: string; }} + * @memberof PinStatus + */ + info?: { [key: string]: string; }; +} + +export function PinStatusFromJSON(json: any): PinStatus { + return PinStatusFromJSONTyped(json, false); +} + +export function PinStatusFromJSONTyped(json: any, ignoreDiscriminator: boolean): PinStatus { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'requestid': json['requestid'], + 'status': StatusFromJSON(json['status']), + 'created': (new Date(json['created'])), + 'pin': PinFromJSON(json['pin']), + 'delegates': json['delegates'], + 'info': !exists(json, 'info') ? undefined : json['info'], + }; +} + +export function PinStatusToJSON(value?: PinStatus | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'requestid': value.requestid, + 'status': StatusToJSON(value.status), + 'created': (value.created.toISOString()), + 'pin': PinToJSON(value.pin), + 'delegates': value.delegates, + 'info': value.info, + }; +} + diff --git a/generated/fetch/src/models/Status.ts b/generated/fetch/src/models/Status.ts new file mode 100644 index 0000000..cd125a1 --- /dev/null +++ b/generated/fetch/src/models/Status.ts @@ -0,0 +1,38 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * IPFS Pinning Service API + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! 🏗️ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. # The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ## Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ## Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ## Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ## Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. # Provider hints A pinning service will use the DHT and other discovery methods to locate pinned content; however, it is a good practice to provide additional provider hints to speed up the discovery phase and start the transfer immediately, especially if a client has the data in their own datastore or already knows of other providers. The most common scenario is a client putting its own IPFS node\'s multiaddrs in `Pin.origins`, and then attempt to connect to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. At the same time, a pinning service will try to connect to multiaddrs provided by the client in `Pin.origins`. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and mutual direct dial between a client and a service works around peer routing issues in restrictive network topologies, such as NATs, firewalls, etc. **NOTE:** Connections to multiaddrs in `origins` and `delegates` arrays should be attempted in best-effort fashion, and dial failure should not fail the pinning operation. When unable to act on explicit provider hints, DHT and other discovery methods should be used as a fallback by a pinning service. **NOTE:** All multiaddrs MUST end with `/p2p/{peerID}` and SHOULD be fully resolved and confirmed to be dialable from the public internet. Avoid sending addresses from local networks. # Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ## Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ## Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * Status a pin object can have at a pinning service + * @export + * @enum {string} + */ +export enum Status { + Queued = 'queued', + Pinning = 'pinning', + Pinned = 'pinned', + Failed = 'failed' +} + +export function StatusFromJSON(json: any): Status { + return StatusFromJSONTyped(json, false); +} + +export function StatusFromJSONTyped(json: any, ignoreDiscriminator: boolean): Status { + return json as Status; +} + +export function StatusToJSON(value?: Status | null): any { + return value as any; +} + diff --git a/generated/fetch/src/models/TextMatchingStrategy.ts b/generated/fetch/src/models/TextMatchingStrategy.ts new file mode 100644 index 0000000..edbc042 --- /dev/null +++ b/generated/fetch/src/models/TextMatchingStrategy.ts @@ -0,0 +1,38 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * IPFS Pinning Service API + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! 🏗️ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. # The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ## Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ## Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ## Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ## Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. # Provider hints A pinning service will use the DHT and other discovery methods to locate pinned content; however, it is a good practice to provide additional provider hints to speed up the discovery phase and start the transfer immediately, especially if a client has the data in their own datastore or already knows of other providers. The most common scenario is a client putting its own IPFS node\'s multiaddrs in `Pin.origins`, and then attempt to connect to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. At the same time, a pinning service will try to connect to multiaddrs provided by the client in `Pin.origins`. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and mutual direct dial between a client and a service works around peer routing issues in restrictive network topologies, such as NATs, firewalls, etc. **NOTE:** Connections to multiaddrs in `origins` and `delegates` arrays should be attempted in best-effort fashion, and dial failure should not fail the pinning operation. When unable to act on explicit provider hints, DHT and other discovery methods should be used as a fallback by a pinning service. **NOTE:** All multiaddrs MUST end with `/p2p/{peerID}` and SHOULD be fully resolved and confirmed to be dialable from the public internet. Avoid sending addresses from local networks. # Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ## Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ## Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * Alternative text matching strategy + * @export + * @enum {string} + */ +export enum TextMatchingStrategy { + Exact = 'exact', + Iexact = 'iexact', + Partial = 'partial', + Ipartial = 'ipartial' +} + +export function TextMatchingStrategyFromJSON(json: any): TextMatchingStrategy { + return TextMatchingStrategyFromJSONTyped(json, false); +} + +export function TextMatchingStrategyFromJSONTyped(json: any, ignoreDiscriminator: boolean): TextMatchingStrategy { + return json as TextMatchingStrategy; +} + +export function TextMatchingStrategyToJSON(value?: TextMatchingStrategy | null): any { + return value as any; +} + diff --git a/generated/fetch/src/models/index.ts b/generated/fetch/src/models/index.ts new file mode 100644 index 0000000..f8f5a52 --- /dev/null +++ b/generated/fetch/src/models/index.ts @@ -0,0 +1,9 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './Failure'; +export * from './FailureError'; +export * from './Pin'; +export * from './PinResults'; +export * from './PinStatus'; +export * from './Status'; +export * from './TextMatchingStrategy'; diff --git a/generated/fetch/src/runtime.ts b/generated/fetch/src/runtime.ts new file mode 100644 index 0000000..daf0911 --- /dev/null +++ b/generated/fetch/src/runtime.ts @@ -0,0 +1,320 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * IPFS Pinning Service API + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! 🏗️ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. # The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ## Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ## Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ## Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ## Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. # Provider hints A pinning service will use the DHT and other discovery methods to locate pinned content; however, it is a good practice to provide additional provider hints to speed up the discovery phase and start the transfer immediately, especially if a client has the data in their own datastore or already knows of other providers. The most common scenario is a client putting its own IPFS node\'s multiaddrs in `Pin.origins`, and then attempt to connect to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. At the same time, a pinning service will try to connect to multiaddrs provided by the client in `Pin.origins`. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and mutual direct dial between a client and a service works around peer routing issues in restrictive network topologies, such as NATs, firewalls, etc. **NOTE:** Connections to multiaddrs in `origins` and `delegates` arrays should be attempted in best-effort fashion, and dial failure should not fail the pinning operation. When unable to act on explicit provider hints, DHT and other discovery methods should be used as a fallback by a pinning service. **NOTE:** All multiaddrs MUST end with `/p2p/{peerID}` and SHOULD be fully resolved and confirmed to be dialable from the public internet. Avoid sending addresses from local networks. # Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ## Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ## Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export const BASE_PATH = "https://pinning-service.example.com".replace(/\/+$/, ""); + +const isBlob = (value: any) => typeof Blob !== 'undefined' && value instanceof Blob; + +/** + * This is the base class for all generated API classes. + */ +export class BaseAPI { + + private middleware: Middleware[]; + + constructor(protected configuration = new Configuration()) { + this.middleware = configuration.middleware; + } + + withMiddleware(this: T, ...middlewares: Middleware[]) { + const next = this.clone(); + next.middleware = next.middleware.concat(...middlewares); + return next; + } + + withPreMiddleware(this: T, ...preMiddlewares: Array) { + const middlewares = preMiddlewares.map((pre) => ({ pre })); + return this.withMiddleware(...middlewares); + } + + withPostMiddleware(this: T, ...postMiddlewares: Array) { + const middlewares = postMiddlewares.map((post) => ({ post })); + return this.withMiddleware(...middlewares); + } + + protected async request(context: RequestOpts, initOverrides?: RequestInit): Promise { + const { url, init } = this.createFetchParams(context, initOverrides); + const response = await this.fetchApi(url, init); + if (response.status >= 200 && response.status < 300) { + return response; + } + throw response; + } + + private createFetchParams(context: RequestOpts, initOverrides?: RequestInit) { + let url = this.configuration.basePath + context.path; + if (context.query !== undefined && Object.keys(context.query).length !== 0) { + // only add the querystring to the URL if there are query parameters. + // this is done to avoid urls ending with a "?" character which buggy webservers + // do not handle correctly sometimes. + url += '?' + this.configuration.queryParamsStringify(context.query); + } + const body = ((typeof FormData !== "undefined" && context.body instanceof FormData) || context.body instanceof URLSearchParams || isBlob(context.body)) + ? context.body + : JSON.stringify(context.body); + + const headers = Object.assign({}, this.configuration.headers, context.headers); + const init = { + method: context.method, + headers: headers, + body, + credentials: this.configuration.credentials, + ...initOverrides + }; + return { url, init }; + } + + private fetchApi = async (url: string, init: RequestInit) => { + let fetchParams = { url, init }; + for (const middleware of this.middleware) { + if (middleware.pre) { + fetchParams = await middleware.pre({ + fetch: this.fetchApi, + ...fetchParams, + }) || fetchParams; + } + } + let response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init); + for (const middleware of this.middleware) { + if (middleware.post) { + response = await middleware.post({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + response: response.clone(), + }) || response; + } + } + return response; + } + + /** + * Create a shallow clone of `this` by constructing a new instance + * and then shallow cloning data members. + */ + private clone(this: T): T { + const constructor = this.constructor as any; + const next = new constructor(this.configuration); + next.middleware = this.middleware.slice(); + return next; + } +}; + +export class RequiredError extends Error { + name: "RequiredError" = "RequiredError"; + constructor(public field: string, msg?: string) { + super(msg); + } +} + +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +export type FetchAPI = GlobalFetch['fetch']; + +export interface ConfigurationParameters { + basePath?: string; // override base path + fetchApi?: FetchAPI; // override for fetch implementation + middleware?: Middleware[]; // middleware to apply before/after fetch requests + queryParamsStringify?: (params: HTTPQuery) => string; // stringify function for query strings + username?: string; // parameter for basic security + password?: string; // parameter for basic security + apiKey?: string | ((name: string) => string); // parameter for apiKey security + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string | Promise); // parameter for oauth2 security + headers?: HTTPHeaders; //header params we want to use on every request + credentials?: RequestCredentials; //value for the credentials param we want to use on each request +} + +export class Configuration { + constructor(private configuration: ConfigurationParameters = {}) {} + + get basePath(): string { + return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH; + } + + get fetchApi(): FetchAPI | undefined { + return this.configuration.fetchApi; + } + + get middleware(): Middleware[] { + return this.configuration.middleware || []; + } + + get queryParamsStringify(): (params: HTTPQuery) => string { + return this.configuration.queryParamsStringify || querystring; + } + + get username(): string | undefined { + return this.configuration.username; + } + + get password(): string | undefined { + return this.configuration.password; + } + + get apiKey(): ((name: string) => string) | undefined { + const apiKey = this.configuration.apiKey; + if (apiKey) { + return typeof apiKey === 'function' ? apiKey : () => apiKey; + } + return undefined; + } + + get accessToken(): ((name?: string, scopes?: string[]) => string | Promise) | undefined { + const accessToken = this.configuration.accessToken; + if (accessToken) { + return typeof accessToken === 'function' ? accessToken : async () => accessToken; + } + return undefined; + } + + get headers(): HTTPHeaders | undefined { + return this.configuration.headers; + } + + get credentials(): RequestCredentials | undefined { + return this.configuration.credentials; + } +} + +export type Json = any; +export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD'; +export type HTTPHeaders = { [key: string]: string }; +export type HTTPQuery = { [key: string]: string | number | null | boolean | Array | HTTPQuery }; +export type HTTPBody = Json | FormData | URLSearchParams; +export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original'; + +export interface FetchParams { + url: string; + init: RequestInit; +} + +export interface RequestOpts { + path: string; + method: HTTPMethod; + headers: HTTPHeaders; + query?: HTTPQuery; + body?: HTTPBody; +} + +export function exists(json: any, key: string) { + const value = json[key]; + return value !== null && value !== undefined; +} + +export function querystring(params: HTTPQuery, prefix: string = ''): string { + return Object.keys(params) + .map((key) => { + const fullKey = prefix + (prefix.length ? `[${key}]` : key); + const value = params[key]; + if (value instanceof Array) { + const multiValue = value.map(singleValue => encodeURIComponent(String(singleValue))) + .join(`&${encodeURIComponent(fullKey)}=`); + return `${encodeURIComponent(fullKey)}=${multiValue}`; + } + if (value instanceof Date) { + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`; + } + if (value instanceof Object) { + return querystring(value as HTTPQuery, fullKey); + } + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`; + }) + .filter(part => part.length > 0) + .join('&'); +} + +export function mapValues(data: any, fn: (item: any) => any) { + return Object.keys(data).reduce( + (acc, key) => ({ ...acc, [key]: fn(data[key]) }), + {} + ); +} + +export function canConsumeForm(consumes: Consume[]): boolean { + for (const consume of consumes) { + if ('multipart/form-data' === consume.contentType) { + return true; + } + } + return false; +} + +export interface Consume { + contentType: string +} + +export interface RequestContext { + fetch: FetchAPI; + url: string; + init: RequestInit; +} + +export interface ResponseContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + response: Response; +} + +export interface Middleware { + pre?(context: RequestContext): Promise; + post?(context: ResponseContext): Promise; +} + +export interface ApiResponse { + raw: Response; + value(): Promise; +} + +export interface ResponseTransformer { + (json: any): T; +} + +export class JSONApiResponse { + constructor(public raw: Response, private transformer: ResponseTransformer = (jsonValue: any) => jsonValue) {} + + async value(): Promise { + return this.transformer(await this.raw.json()); + } +} + +export class VoidApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return undefined; + } +} + +export class BlobApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.blob(); + }; +} + +export class TextApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.text(); + }; +} diff --git a/openapitools.json b/openapitools.json index 3abec70..9f18f01 100644 --- a/openapitools.json +++ b/openapitools.json @@ -7,7 +7,7 @@ "storageDir": "", "generators": { "node": { - "inputSpec": "https://raw.githubusercontent.com/ipfs/pinning-services-api-spec/main/ipfs-pinning-service.yaml", + "inputSpec": "https://raw.githubusercontent.com/ipfs/pinning-services-api-spec/v1.0.0/ipfs-pinning-service.yaml", "strictSpec": true, "apiNameSuffix": "", "generatorName": "typescript-node", @@ -27,7 +27,7 @@ } }, "fetch": { - "inputSpec": "https://raw.githubusercontent.com/ipfs/pinning-services-api-spec/main/ipfs-pinning-service.yaml", + "inputSpec": "https://raw.githubusercontent.com/ipfs/pinning-services-api-spec/v1.0.0/ipfs-pinning-service.yaml", "strictSpec": true, "apiNameSuffix": "", "generatorName": "typescript-fetch", @@ -47,7 +47,7 @@ } }, "ts": { - "inputSpec": "https://raw.githubusercontent.com/ipfs/pinning-services-api-spec/main/ipfs-pinning-service.yaml", + "inputSpec": "https://raw.githubusercontent.com/ipfs/pinning-services-api-spec/v1.0.0/ipfs-pinning-service.yaml", "strictSpec": true, "apiNameSuffix": "", "generatorName": "typescript", diff --git a/package.json b/package.json index 21aa520..6e64ab4 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,13 @@ "bugs": { "url": "https://github.com/ipfs-shipyard/js-pinning-service-http-client/issues" }, + "keywords": [ + "dweb", + "ipfs", + "pinning service", + "protocol labs", + "web3" + ], "engines": { "node": ">=16.0.0", "npm": ">=7.0.0" @@ -124,39 +131,61 @@ ] }, "scripts": { + "clean": "npx rimraf dist generated node_modules", + "ci:test": "run-s test:*", "dep-check": "aegir dep-check src/**/*.ts test/**/*.ts generated/**/*.ts", "fix": "run-s fix:*", "fix:lint": "aegir lint --fix", "lint": "run-s lint:*", "lint:main": "aegir lint", - "lint:project": "check-aegir-project", + "lint:ts": "aegir ts -p check", + "lint-TODO:project": "check-aegir-project # currently broken due to corrupting the repoUrl", "release": "aegir release", "postinstall": "run-s gen", "build": "aegir build", + "build2": "tsc-silent -p tsconfig.generated.json --suppress 6133@generated 6192@generated --stats", + "build:main": "aegir build", + "build:docs": "aegir ts docs", + "build:types": "aegir ts -p types", "test": "run-s test:*", + "test:browser": "aegir test --target browser", + "test:webworker": "aegir test --target webworker", "test:electron": "aegir test --target electron-main", "test:node": "aegir test --target node --cov && npx nyc report", - "pregen:node": "openapi-generator-cli validate -i https://raw.githubusercontent.com/ipfs/pinning-services-api-spec/main/ipfs-pinning-service.yaml", - "gen": "run-p gen:*", + "pregen": "openapi-generator-cli validate -i https://raw.githubusercontent.com/ipfs/pinning-services-api-spec/v1.0.0/ipfs-pinning-service.yaml", + "gen": "run-p gen:fetch", + "postgen": "tsc-silent -p tsconfig.generated.json --suppress 6133@generated 6192@generated --stats", "gen:fetch": "openapi-generator-cli generate --generator-key fetch", "gen:node": "openapi-generator-cli generate --generator-key node", "gen:ts": "openapi-generator-cli generate --generator-key ts" }, "dependencies": { - "node-fetch": "^3.2.0" + "fetch-ponyfill": "^7.1.0", + "yargs": "^17.3.1" }, "devDependencies": { "@openapitools/openapi-generator-cli": "^2.4.26", + "@swc/core": "^1.2.144", + "@swc/helpers": "^0.3.3", + "@types/cors": "^2.8.12", "@types/express": "^4.17.13", "@types/mocha": "^9.1.0", - "@types/node": "^17.0.16", - "@types/node-fetch": "^3.0.3", + "@types/node": "^17.0.21", + "@types/portscanner": "^2.1.1", "aegir": "^36.1.3", "check-aegir-project": "^1.0.3", + "cors": "^2.8.5", "dotenvrc": "^1.0.1", + "express": "^4.17.3", + "express-promise-router": "^4.1.1", "mock-ipfs-pinning-service": "^0.4.0", "npm-run-all": "^4.1.5", - "openapi-typescript": "^5.1.1" + "openapi-typescript": "^5.1.1", + "portscanner": "^2.2.0", + "regenerator-runtime": "^0.13.9", + "ts-node": "^10.5.0", + "tsc-silent": "^1.2.1", + "winston": "^3.6.0" }, "exports": { ".": { diff --git a/patches/aegir+36.1.3.patch b/patches/aegir+36.1.3.patch new file mode 100644 index 0000000..0a644a4 --- /dev/null +++ b/patches/aegir+36.1.3.patch @@ -0,0 +1,15 @@ +diff --git a/node_modules/aegir/src/build/index.js b/node_modules/aegir/src/build/index.js +index a614f51..e455dfb 100644 +--- a/node_modules/aegir/src/build/index.js ++++ b/node_modules/aegir/src/build/index.js +@@ -130,9 +130,7 @@ const tasks = new Listr([ + */ + task: async (ctx, task) => { + await tsCmd({ +- debug: ctx.debug, +- tsRepo: ctx.tsRepo, +- fileConfig: ctx.fileConfig, ++ ...ctx, + preset: 'types', + include: ctx.fileConfig.ts.include, + copyTo: ctx.fileConfig.ts.copyTo, diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..4c723e9 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,35 @@ +import fetchPonyfill from 'fetch-ponyfill' +import { Configuration as GeneratedConfiguration } from '../generated/fetch/dist/src' +import type { ConfigurationParameters } from '../generated/fetch/dist/src' + +export * from '../generated/fetch/dist/src/apis' +export * from '../generated/fetch/dist/src/models' + +export class Configuration extends GeneratedConfiguration { + constructor (options: ConfigurationParameters) { + /** + * Prevent the need for everyone to have to override the fetch API... + */ + if (options.fetchApi == null) { + options.fetchApi = fetchPonyfill().fetch + } + super(options) + } +} + +export type { ConfigurationParameters } +export { + BASE_PATH, + BaseAPI, + BlobApiResponse, + COLLECTION_FORMATS, + // Configuration, // overwritten above + JSONApiResponse, + RequiredError, + TextApiResponse, + VoidApiResponse, + canConsumeForm, + exists, + mapValues, + querystring +} from '../generated/fetch/dist/src/runtime' diff --git a/test/MockServer.ts b/test/MockServer.ts new file mode 100644 index 0000000..e820cd3 --- /dev/null +++ b/test/MockServer.ts @@ -0,0 +1,146 @@ +import { setup } from 'mock-ipfs-pinning-service' +import type { Application } from 'express' +import type { Server } from 'http' +import portscanner from 'portscanner' +import cors from 'cors' +import { logger } from './logger' + +try { + import('dotenvrc') +} catch (err) { + // no dotenvrc.. that's okay + // eslint-disable-next-line no-console + console.warn('No .envrc file found; assuming CI environment. If not: You should copy .envrc-copy and set your environment variables.') +} + +export class MockServer { + private _server: Server | undefined = undefined + + private port: string = '3001' + private _service: Application | undefined = undefined + + public basePath: string = '' + + constructor (private readonly config: Parameters[0] = { token: process.env.MOCK_PINNING_SERVER_SECRET }) { + process.on('uncaughtException', MockServer.onEADDRINUSE) + } + + public async start (port = this.port): Promise { + let server + try { + server = await this.server(port) + } catch (err) { + logger.error('start error', err) + } + + if (server == null) { + process.exit(1) + } + const handler = this.cleanupSync.bind(this) + // And you'll want to make sure you close the server when your process exits + process.on('beforeExit', handler) + process.on('SIGTERM', handler) + process.on('SIGINT', handler) + process.on('SIGHUP', handler) + + // To prevent duplicated cleanup, remove the process listeners on server close. + server.on('close', () => { + process.off('beforeExit', handler) + process.off('SIGTERM', handler) + process.off('SIGINT', handler) + process.off('SIGHUP', handler) + }) + } + + public async stop (): Promise { + await this.cleanup() + } + + private async server (port = this.port): Promise { + if (this._server !== undefined) { + return this._server + } + if (port != null && port !== this.port) { + this.port = port + this.setBasePath() + } else { + port = await this.getAvailablePort() + } + const service = await this.service() + + service.on('error', (err) => { + logger.error('service error', err) + }) + this._server = service.listen(port, () => { + logger.debug(`${Date.now()}: MockServer running on port ${port}`) + }) + + return this._server + } + + /** + * Ensure the port set for this instance is not already in use by another MockServer + */ + private async getAvailablePort (): Promise { + return await new Promise((resolve, reject) => portscanner.findAPortNotInUse(3000, 3099, '127.0.0.1', (error, port) => { + if (error != null) { + return reject(error) + } + this.port = port.toString() + this.setBasePath() + resolve(this.port) + })) + } + + private async service (): Promise { + if (this._service !== undefined) { + return this._service + } + this._service = await setup(this.config) + + this._service.use(cors()) + + return this._service + } + + private setBasePath (): void { + if (this.port == null) { + throw new Error('Attempted to set basePath before setting this.port.') + } + this.basePath = `http://127.0.0.1:${this.port}` + } + + private cleanupSync (): void { + void this.cleanup().then(() => { + logger.debug('cleaned up') + }) + } + + // Express server cleanup handling. + private async cleanup (): Promise { + const server = await this.server() + const port = this.port + server.close((err) => { + if ((err == null) || (err as Error & { code: string })?.code === 'ERR_SERVER_NOT_RUNNING') { + // MockServer.portsInUse.remove(this.port) + logger.debug(`${Date.now()}: MockServer stopped listening on port ${port}`) + delete this._server + } else if (err != null) { + throw err + } + }) + } + + private static onEADDRINUSE (err: Error & { code: string }) { + if (err.code === 'EADDRINUSE') { + logger.error('Unexpected conflict with port usage') + } else { + logger.error('CAUGHT UNKNOWN ERROR') + logger.error(err.name) + logger.error(err.code) + logger.error(err.message) + logger.error(err.stack) + } + process.exit(1) + } +} diff --git a/test/MockServerController.ts b/test/MockServerController.ts new file mode 100644 index 0000000..1544f20 --- /dev/null +++ b/test/MockServerController.ts @@ -0,0 +1,123 @@ +import express from 'express' +import { MockServer } from './MockServer' +import Router from 'express-promise-router' +import cors from 'cors' +import { logger } from './logger' + +/** + * MockServerController stands up a server on port 3000 + */ +class MockServerController { + private readonly mockServers: MockServer[] = [] + private readonly app = express() + private readonly router = Router() + + private readonly port = 3000 + server: import('http').Server + constructor () { + this.router.get<'/start', {port?: string}>('/start', async (req, res, next) => { // eslint-disable-line @typescript-eslint/no-misused-promises + const { port } = req.params + + let mockServer: MockServer | null = null + try { + mockServer = await this.startIpfsPinningServer(port) + this.mockServers.push(mockServer) + + /** + * We need to return the basePath and accessToken so the client can call the correct mockServer + */ + res.send({ + success: true, + basePath: mockServer.basePath, + accessToken: process.env.MOCK_PINNING_SERVER_SECRET + }) + } catch (error) { + res.json({ success: false, error }) + next(error) + } + }) + + /** + * A client will request to shut down it's mockServer by port, which it should have received upon calling '/start' + */ + this.router.get<'/stop/:port', {port: string}>('/stop/:port', async (req, res, next) => { // eslint-disable-line @typescript-eslint/no-misused-promises + const { port } = req.params + + const mockServer = this.mockServers.find((mockS) => mockS.basePath.includes(port)) + + if (mockServer != null) { + try { + await mockServer.stop() + res.json({ success: true }) + } catch (error) { + res.json({ success: false, error }) + next(error) + } + } else { + logger.error('Could not get mockserver') + throw new Error(`MockServer at port ${port} could not be found`) + } + }) + + this.app.use(cors()) + this.app.use(this.router) + + this.server = this.app.listen(this.port, () => { + logger.debug(`MockServerController listening on port ${this.port}`) + }) + + // And you'll want to make sure you close the server when your process exits + process.on('beforeExit', this.shutdownSync) + process.on('SIGTERM', this.shutdownSync) + process.on('SIGINT', this.shutdownSync) + process.on('SIGHUP', this.shutdownSync) + + this.server.on('close', () => { + logger.debug(`MockServerController stopped listening on ${this.port}`) + }) + } + + private shutdownSync () { + this.shutdown().catch((err) => { + logger.error(err) + }) + } + + async shutdown () { + // To prevent duplicated cleanup, remove the process listeners on server close. + process.off('beforeExit', this.shutdownSync) + process.off('SIGTERM', this.shutdownSync) + process.off('SIGINT', this.shutdownSync) + process.off('SIGHUP', this.shutdownSync) + await new Promise((resolve, reject) => { + this.server.close((err) => { + if (err != null) { + logger.error('Unexpected error when shutting down the MockServerController') + logger.error(err) + } else { + logger.debug(`MockServerController stopped listening on port ${this.port}`) + } + resolve() + }) + }) + for await (const mockS of this.mockServers) { + try { + await mockS.stop() + } catch (err) { + logger.error(`Unexpected error when attempting to shutdown mock server at ${mockS.basePath}`) + logger.error(err) + } + } + } + + private async startIpfsPinningServer (port?: string) { + const mockServer = new MockServer({ + token: process.env.MOCK_PINNING_SERVER_SECRET + }) + await mockServer.start(port) + + return mockServer + } +} + +export { MockServerController } diff --git a/test/client.spec.ts b/test/client.spec.ts new file mode 100644 index 0000000..d6c4fa3 --- /dev/null +++ b/test/client.spec.ts @@ -0,0 +1,73 @@ +/* eslint-env browser, node, mocha */ + +import { expect } from 'aegir/utils/chai' +import { Configuration, PinsApi, Status } from '../src' +import type { Pin } from '../src' +import fetchPonyfill from 'fetch-ponyfill' + +const { fetch } = fetchPonyfill() + +let Config = new Configuration({ + basePath: 'http://127.0.0.1:3000', + // fetchApi: fetch, + accessToken: process.env.MOCK_PINNING_SERVER_SECRET +}) +describe('Client', () => { + it('Can be instantiated', () => { + expect(() => new PinsApi(Config)).not.to.throw() + }) + + describe('Operations', () => { + // let mockServer: MockServer + // let Config: Configuration + beforeEach(async () => { + const response = await fetch('http://localhost:3000/start') + const { basePath, accessToken } = await response.json() + Config = new Configuration({ + basePath, + accessToken + // fetchApi: fetch + }) + }) + + afterEach(async () => { + const [,,port] = Config.basePath.split(':') + const response = await fetch(`http://localhost:3000/stop/${port}`) + + const { success } = await response.json() + + if (success === false) { + throw new Error(`Unexpected error when attempting to stop the mockServer on port ${port}`) + } + }) + + it('GET: Can get failed Pins', async () => { + const Client = new PinsApi(Config) + const response = await Client.pinsGet({ limit: 1, status: new Set([Status.Failed]) }) + expect(response).to.deep.eq({ count: 0, results: new Set() }) + }) + + it('GET: Can add a Pin successfully', async () => { + const Client = new PinsApi(Config) + const pin: Pin = { + cid: 'abc123', + name: 'pinned-test1' + } + const response = await Client.pinsPost({ pin }) + expect(response).to.deep.includes({ status: Status.Pinned }) + expect(response.pin).to.deep.include({ ...pin }) + }) + + it('POST: Can handle a failed pinning', async () => { + const Client = new PinsApi(Config) + const pin: Pin = { + cid: 'abc123', + name: 'failed-test2' + } + const response = await Client.pinsPost({ pin }) + expect(response).to.deep.includes({ status: Status.Failed }) + expect(response.pin).to.deep.include({ ...pin }) + }) + }) +}) +// } diff --git a/test/configuration.spec.ts b/test/configuration.spec.ts new file mode 100644 index 0000000..a2b4587 --- /dev/null +++ b/test/configuration.spec.ts @@ -0,0 +1,13 @@ +/* eslint-env browser, node, mocha */ + +import { expect } from 'aegir/utils/chai' +import { Configuration, ConfigurationParameters } from '../src' + +// export default async (setup: () => Promise) => { +describe('Configuration', () => { + it('Can be instantiated', () => { + const configuration: ConfigurationParameters = {} + expect(() => new Configuration(configuration)).not.to.throw() + }) +}) +// } diff --git a/test/logger.ts b/test/logger.ts new file mode 100644 index 0000000..fc859b2 --- /dev/null +++ b/test/logger.ts @@ -0,0 +1,19 @@ +import winston from 'winston' + +const logLevel = process.env.LOG_LEVEL ?? 'error' + +const transports = { + console: new winston.transports.Console({ level: logLevel }) +} + +const logger = winston.createLogger({ + transports: [ + transports.console + ] +}) + +if (process.env.LOG_LEVEL == null) { + logger.silent = true +} + +export { logger } diff --git a/test/node-tests/.gitkeep b/test/node-tests/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/node-tests/index.ts b/test/node-tests/index.ts new file mode 100644 index 0000000..83b6c25 --- /dev/null +++ b/test/node-tests/index.ts @@ -0,0 +1 @@ +export * from './mockServerController' diff --git a/test/node-tests/mockServerController.ts b/test/node-tests/mockServerController.ts new file mode 100755 index 0000000..3a74518 --- /dev/null +++ b/test/node-tests/mockServerController.ts @@ -0,0 +1,41 @@ +import { MockServerController } from '../MockServerController' + +import fetchPonyfill from 'fetch-ponyfill' +import { expect } from 'aegir/utils/chai' + +const { fetch } = fetchPonyfill() + +export default async (setup: () => Promise) => { + describe.skip('MockServerController', () => { + it('can start and stop without errors', async () => { + expect(async () => { + const controller = new MockServerController() + await controller.shutdown() + }).not.to.throw() + }) + + it('Can start multiple mockServers', async () => { + const controller = new MockServerController() + const serverConfigs: Array<{basePath: string}> = [] + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + for await (const _ of Array(5)) { + const response = await fetch('http://localhost:3000/start') + serverConfigs.push(await response.json()) + } + // it('Can shutdown those mockServers', async () => { + for await (const config of serverConfigs) { + const { basePath } = config + const [,, port] = basePath + + const response = await fetch(`http://localhost:3000/stop/${port}`) + + const { success } = await response.json() + expect(success).to.equal(true) + } + // }) + + await controller.shutdown() + }) + }) +} diff --git a/tsconfig.generated.json b/tsconfig.generated.json new file mode 100644 index 0000000..7a1d882 --- /dev/null +++ b/tsconfig.generated.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "sourceRoot": "generated/fetch/src", + "outDir": "generated/fetch/dist", + "emitDeclarationOnly": false, + "module": "ES2020", + "target": "ES2020", + "typeRoots": [ + "./types", + "./node_modules/@types" + ], + "declaration": true, + "noEmitOnError": true, + "esModuleInterop": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "isolatedModules": false, + "rootDir": "generated/fetch", + "sourceMap": true, + "moduleResolution":"Node" + + }, + "include": [ + "generated/fetch/src", + "types/global.d.ts", + ], + "exclude": [ + "./generated/fetch/dist" + ] +} diff --git a/tsconfig.json b/tsconfig.json index 2f1d962..6f2094a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "./node_modules/aegir/src/config/tsconfig.aegir.json", + "extends": "aegir/src/config/tsconfig.aegir.json", "compilerOptions": { "outDir": "dist", "emitDeclarationOnly": false, @@ -7,13 +7,35 @@ "typeRoots": [ "./types", "./node_modules/@types", + "./generated/fetch/dist" ], + "noEmitOnError": true, + "esModuleInterop": true, + "noUnusedLocals": false, + "noUnusedParameters": false }, "include": [ "src", - "generated", "test", - "generated", - "types", + "test/**/*.ts", + "types/**/*.d.ts", + "MockServer.ts", + "./generated/fetch/dist" ], + "exclude": [ + "generated" + ], + "ts-node": { + "transpileOnly": true, + "files": true, + "emit": false, + "compiler": "typescript", + "compilerHost": false, + "compilerOptions": { + "module": "CommonJS" + }, + "preferTsExts": true, + "pretty": true, + "swc": true + } } diff --git a/types/global.d.ts b/types/global.d.ts index 3dd92a5..95bbbe0 100644 --- a/types/global.d.ts +++ b/types/global.d.ts @@ -1,3 +1,2 @@ declare type GlobalFetch = WindowOrWorkerGlobalScope - diff --git a/types/mock-ipfs-pinning-service.d.ts b/types/mock-ipfs-pinning-service.d.ts index 0f14aaf..3f2d0eb 100644 --- a/types/mock-ipfs-pinning-service.d.ts +++ b/types/mock-ipfs-pinning-service.d.ts @@ -1,11 +1,4 @@ -import type { Application } from 'express'; -export module 'mock-ipfs-pinning-service' { - declare const MockIpfsPinningService = { - setup: () => Application, - } - export default { - setup: express.application, - }; - export default MockIpfsPinningService; +declare module 'mock-ipfs-pinning-service' { + export * from 'mock-ipfs-pinning-service/dist/index' }