Skip to content

Commit

Permalink
Merge pull request #25 from comake/version/2.2.0
Browse files Browse the repository at this point in the history
Version/2.2.0
  • Loading branch information
adlerfaulkner committed Jun 22, 2023
2 parents 53293f5 + ab05f2d commit bfae590
Show file tree
Hide file tree
Showing 18 changed files with 335 additions and 548 deletions.
57 changes: 56 additions & 1 deletion docs/get-started/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,59 @@ The Ticketmaster OpenAPI spec we supplied to Standard SDK describes an operation
Each operation was supplied relevant arguments as the first parameter and configuration as the second. See the [standardSDKInstance.\<namespace\>.\<operation\>](../reference/api-reference.md#standardsdkinstancenamespaceoperationargs-configuration-options) api reference for more information on these parameters.

### OpenAPI Directory
For your convenience, we have compiled a [directory of OpenAPI specs](https://github.com/comake/openapi-directory) of many popular APIs that you can use in your projects. We'd like to acknowledge [APIs.guru](https://apis.guru/), who supplied most of the API specs in the directory. Please submit any missing OpenAPI specs or ones you create as PRs to the directory. That way other developers can re-use them!
For your convenience, we have compiled a [directory of OpenAPI specs](https://github.com/comake/openapi-directory) of many popular APIs that you can use in your projects. We'd like to acknowledge [APIs.guru](https://apis.guru/), who supplied most of the API specs in the directory. Please submit any missing OpenAPI specs or ones you create as PRs to the directory. That way other developers can re-use them!

### Default Configuration and Options

If your API requires certain security credentials or headers on all requests, it may get repetitive to supply those on every operation you execute. Instead, you can set default configuration either when you build your SDK, and/or at any other time after the SDK is built.

When building your SDK, you can supply optional parameters `defaultConfiguration` and `defaultOptions` for each API.
```typescript
import { StandardSDK } from '@comake/standard-sdk-js';
import ticketmasterOpenApiSpec from './path/to/ticketmaster-openapi-spec.ts';

// Build the Standard SDK with your API specs of choice
const standardSdk = StandardSDK.build({
apiSpecs: {
ticketmaster: {
type: 'openapi',
value: ticketmasterOpenApiSpec,
defaultConfiguration: {
apiKey: 'abc123'
},
defaultOptions: {
headers: {
'X-Powered-By': 'Comake'
}
}
}
},
});
```

After building your SDK, you can use the `setDefaultConfiguration` and `setDefaultOptions` methods to update the configuration and options applied to every operation you execute with an API.

```typescript
import { StandardSDK } from '@comake/standard-sdk-js';
import ticketmasterOpenApiSpec from './path/to/ticketmaster-openapi-spec.ts';

// Build the Standard SDK with your API specs of choice
const standardSdk = StandardSDK.build({
apiSpecs: {
ticketmaster: {
type: 'openapi',
value: ticketmasterOpenApiSpec
}
},
});

standardSdk.ticketmaster.setDefaultConfiguration({
apiKey: 'abc123'
});

standardSdk.ticketmaster.setDefaultOptions({
headers: {
'X-Powered-By': 'Comake'
}
});
```
63 changes: 50 additions & 13 deletions docs/reference/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ The `build` method takes one argument which may have either or both of `apiSpecs

`ApiSpecOptions` are the configurations you pass when building a Standard SDK for the APIs you want to work with. Different types of APIs have their own specific options. Currently we only support OpenAPI specs.

**Common ApiSpecOptions fields**

- `type` - A type of API specification. Possible values are: `openapi`
- `value` - The contents of the API specification. Usually a string or JSON object.
| Parameter | Required | Description |
| --- | --- | ------------ |
| `type` | Required | A type of API specification. Possible values are: `openapi` |
| `value` | Required | The contents of the API specification. Usually a string or JSON object. When using Typescript, use `as const` on your API spec to get extended type support. |
| `defaultConfiguration` | | Optional configuration that will be added to every operation of this API. For OpenAPI specs, this field will be an `OpenApiClientConfiguration` object as defined in [@comake/openapi-operation-executor](https://github.com/comake/openapi-operation-executor). |
| `defaultOptions` | | Optional options that will be applied to every operation of this API. For OpenAPI operations, this is an `AxiosRequestConfig` object. See the [axios API documentation](https://github.com/axios/axios#request-config) for reference. |

#### Return Value

A `StandardSDK` instance with a namespace for each key in `apiSpecs`, if supplied. May also have an `skl` property if `sklEngineOptions` was supplied.
A `StandardSDK` instance with a namespace for each key in `apiSpecs`. It may also have an `skl` property if `sklEngineOptions` was supplied.

#### Example Usage

Expand All @@ -37,36 +39,71 @@ const standardSdk = StandardSDK.build({
ticketmaster: {
type: 'openapi',
value: ticketmasterOpenApiSpec,
defaultConfiguration: {
apiKey: 'abc123'
},
defaultOptions: {
headers: {
'X-Powered-By': 'Comake'
}
}
},
},
});
```

## [`standardSDKInstance.<namespace>`](#standardsdkinstancenamespace)
## [`standardSDKInstance.<namespace>`](#standardsdkinstance-namespace)

A namespace of a `StandardSDK` instance corresponding to a set of supplied Api Spec Options. Has properties for each operation in the supplied API specification.
A namespace of a `StandardSDK` instance corresponding to a set of supplied Api Spec Options. A namespace has a property for each operation in the supplied API specification. Each namespace also includes methods `setDefaultConfiguration` and `setDefaultOptions` to update the default configuration and options to all operations if the API.

#### Example Usage
In Typescript:
```typescript
const sdkNamespace = standardSdk.ticketmaster;
```

## [`standardSDKInstance.<namespace>.<operation>(args, configuration, options)`](#standardsdkinstancenamespaceoperationargs-configuration-options)
## [`standardSDKInstance.<namespace>.setDefaultConfiguration(configuration)`](#standardsdkinstance-namespace-setDefaultConfiguration)

Sets the default configuration for all operations of in an API namespace. When using an API described via an OpenAPI spec, the `configuration` parameter should be a `OpenApiClientConfiguration` object as defined in [@comake/openapi-operation-executor](https://github.com/comake/openapi-operation-executor).

#### Example Usage
In Typescript:
```typescript
standardSdk.ticketmaster.setDefaultConfiguration({
apiKey: 'acb123'
});
```

## [`standardSDKInstance.<namespace>.setDefaultOptions(options)`](#standardsdkinstance-namespace-setDefaultConfiguration)

Sets the default options for all operations of in an API namespace. When using an API described via an OpenAPI spec, the `options` parameter should be an `AxiosRequestConfig` object as defined in the [axios API documentation](https://github.com/axios/axios#request-config).

#### Example Usage
In Typescript:
```typescript
standardSdk.ticketmaster.setDefaultOptions({
headers: {
'X-Powered-By': 'Comake'
}
});
```


## [`standardSDKInstance.<namespace>.<operation>(args, configuration, options)`](#standardsdkinstance-namespace-operation)

Executes the API operation called `<operation>` according to the API specification corresponding to the namespace `<namespace>`.

#### Parameters

All parameters are not required.
All parameters are optional, however, when using Typescript and executing an OpenAPI operation with required parameters or request body, the `args` parameter will be required.

| Parameter | Type | Description &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |
| :--- | :--- | :--- |
| `args` | `object` | Arguments for the operation as defined by the API specification. For example, an operation in an OpenAPI spec has either a `requestBody` or a `parameters` field. StandardSDK will read these fields and construct and send a web request with the data from `args` in the correct parts of the request. |
| `configuration` | `object` | An object holding any configuration necessary for the operation. Most often this includes security credentials. For example, for an OpenAPI operation `configuration` may include an `apiKey` or `accessToken` to satisfy the [Security Requirement](https://spec.openapis.org/oas/v3.1.0#securityRequirementObject) of the operation. See note below for more information. |
| `options` | `object` | An object holding any options to supply to the operation execution module to modfy default behaviors the request or message sent for the operation. For OpenAPI operations, this is an `AxiosRequestConfig` object. See the [axios API documentation](https://github.com/axios/axios#request-config) for reference. |

⚠️ StandardSDK uses the [@comake/openapi-operation-executor](https://www.npmjs.com/package/@comake/openapi-operation-executor) package to execute OpenAPI operations. This library currently supports OpenAPI security types `oauth2`, `apiKey`, and `http` with scheme `basic`. See [the OpenAPI Spec](https://spec.openapis.org/oas/v3.1.0#security-scheme-object) for reference and the [@comake/openapi-operation-executor API docs](https://github.com/comake/openapi-operation-executor#api) for more information.
⚠️ StandardSDK uses the [@comake/openapi-operation-executor](https://www.npmjs.com/package/@comake/openapi-operation-executor) package to execute OpenAPI operations. This library currently supports OpenAPI security types `oauth2`, `apiKey`, and `http` with schemes `basic` or `bearer`. See [the OpenAPI Spec](https://spec.openapis.org/oas/v3.1.0#security-scheme-object) for reference and the [@comake/openapi-operation-executor API docs](https://github.com/comake/openapi-operation-executor#api) for more information.

#### Return Value

Expand All @@ -83,7 +120,7 @@ const axiosResponse = await standardSdk.ticketmaster.SearchEvents(
);
```

## [`standardSDKInstance.skl`](#standardsdkinstanceskl)
## [`standardSDKInstance.skl`](#standardsdkinstance-skl)

A [SKL JS Engine](https://www.npmjs.com/package/@comake/skl-js-engine) instance which is instantiated when the `sklEngineOptions` parameter is supplied when Standard SDK is built.

Expand All @@ -95,7 +132,7 @@ In Typescript:
const skl = standardSdk.skl;
```

## [`standardSDKInstance.skl.verb`](#standardsdkinstancesklverb)
## [`standardSDKInstance.skl.verb`](#standardsdkinstance-skl-verb)

The [Verb](https://docs.standardknowledge.com/fundamentals#verbs) execution interface of the [SKL JS Engine](https://www.npmjs.com/package/@comake/skl-js-engine) instance accessed through Standard SDK.

Expand All @@ -107,7 +144,7 @@ In Typescript:
const skl = standardSdk.skl.verb;
```

## [`standardSDKInstance.skl.verb.<verb>(args)`](#standardsdkinstancesklverbverb)
## [`standardSDKInstance.skl.verb.<verb>(args)`](#standardsdkinstance-skl-verb-verb)

Executes the verb called `<verb>` according to its Schema and related Mappings in the Schemas provided to the [SKL JS Engine](https://www.npmjs.com/package/@comake/skl-js-engine) instance via the `sklEngineOptions` parameter when building Standard SDK.

Expand Down
File renamed without changes.
5 changes: 3 additions & 2 deletions src/ApiOperationNamespace.ts → src/ApiNamespace.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { ApiSpecOptions } from './ApiSpecOptions';
import type { OpenApiNamespace } from './openapi-types/OpenApiNamespace';
import type { OpenApiOperationNamespace } from './openapi-types/OpenApiOperationNamespace';

export type ApiOperationNamespace<
export type ApiNamespace<
T extends ApiSpecOptions
> = T['type'] extends 'openapi'
? OpenApiOperationNamespace<T['value'], T['defaultConfiguration']>
? OpenApiOperationNamespace<T['value']> & OpenApiNamespace
: never;
25 changes: 25 additions & 0 deletions src/BaseApiNamespace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { OpenApiClientConfiguration } from '@comake/openapi-operation-executor';
import type { AxiosRequestConfig } from 'axios';

interface BaseApiNamespaceArgs {
defaultConfiguration?: OpenApiClientConfiguration;
defaultOptions?: AxiosRequestConfig;
}

export class BaseApiNamespace {
public defaultConfiguration: Record<string, any> = {};
public defaultOptions: AxiosRequestConfig = {};

public constructor(args: BaseApiNamespaceArgs) {
this.defaultConfiguration = args.defaultConfiguration ?? {};
this.defaultOptions = args.defaultOptions ?? {};
}

public setDefaultConfiguration(configuration: Record<string, any>): void {
this.defaultConfiguration = configuration;
}

public setDefaultOptions(options: AxiosRequestConfig): void {
this.defaultOptions = options;
}
}
9 changes: 9 additions & 0 deletions src/BaseApiSpecOptions.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { AxiosRequestConfig } from 'axios';
import type { ApiSpecType } from './ApiSpecOptions';

export interface BaseApiSpecOptions {
Expand All @@ -11,4 +12,12 @@ export interface BaseApiSpecOptions {
* type as defined in \@comake/openapi-operation-executor
*/
readonly value: string | Record<string, unknown>;
/**
* Default configuration supplied to every operation of this API.
*/
readonly defaultConfiguration?: Record<string, any>;
/**
* Default Axios Request Options supplied to every operation of this API.
*/
readonly defaultOptions?: AxiosRequestConfig;
}
8 changes: 7 additions & 1 deletion src/OperationHandler.ts
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
export type OperationHandler = (args: any, configuration: any, options?: any) => Promise<any>;
import type { AxiosRequestConfig } from 'axios';

export type OperationHandler = (
args?: Record<string, any>,
configuration?: Record<string, any>,
options?: AxiosRequestConfig,
) => Promise<any>;
47 changes: 24 additions & 23 deletions src/StandardSdk.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { OpenApiClientConfiguration } from '@comake/openapi-operation-executor';
import type { SKLEngineOptions } from '@comake/skl-js-engine';
import { SKLEngine } from '@comake/skl-js-engine';
import type { ApiOperationNamespace } from './ApiOperationNamespace';
import type { ApiNamespace } from './ApiNamespace';
import type { ApiSpecOptions, ApiSpecs } from './ApiSpecOptions';
import { BaseApiNamespace } from './BaseApiNamespace';
import { OpenApiOperationExecutor } from './operation-executor/OpenApiOperationExecutor';
import type { OperationExecutor } from './operation-executor/OperationExecutor';
import type { OperationHandler } from './OperationHandler';
Expand All @@ -23,7 +23,7 @@ class StandardSDKBase<T extends ApiSpecs> {

public constructor(args: StandardSdkArgs<T>) {
if (args.apiSpecs) {
const apiOperationNamespaces = this.createApiOperationNamespaces(args.apiSpecs);
const apiOperationNamespaces = this.createApiNamespaces(args.apiSpecs);
Object.assign(this, apiOperationNamespaces);
}
if (args.sklEngineOptions) {
Expand All @@ -38,25 +38,23 @@ class StandardSDKBase<T extends ApiSpecs> {
throw new Error('Failed to access skl. No `sklEngineOptions` found on initialization of StandardSDK.');
}

private createApiOperationNamespaces<TS extends ApiSpecs>(
private createApiNamespaces<TS extends ApiSpecs>(
apiSpecs: TS,
): Record<keyof TS, ApiOperationNamespace<ApiSpecOptions>> {
): Record<keyof TS, ApiNamespace<ApiSpecOptions>> {
return Object.entries(apiSpecs)
.reduce(<TR extends ApiSpecOptions>(
obj: Record<keyof TS, ApiOperationNamespace<ApiSpecOptions>>,
obj: Record<keyof TS, ApiNamespace<ApiSpecOptions>>,
[ apiSpecName, specObject ]: [string, TR],
): Record<keyof TS, ApiOperationNamespace<ApiSpecOptions>> => {
): Record<keyof TS, ApiNamespace<ApiSpecOptions>> => {
const executor = this.generateExecutorForApiSpecOptions(specObject);
const operationHandler = this.buildOperationHandlerForApiSpec(executor, specObject.defaultConfiguration);
const operationHandler = this.buildOperationHandlerForApiSpec(executor);
const baseOperationNamespace = new BaseApiNamespace(specObject);
return {
...obj,
[apiSpecName]: new Proxy(
{} as ApiOperationNamespace<ApiSpecOptions>,
{ get: operationHandler },
),
[apiSpecName]: new Proxy(baseOperationNamespace, { get: operationHandler }),
};
// eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter
}, {} as Record<keyof TS, ApiOperationNamespace<ApiSpecOptions>>);
}, {} as Record<keyof TS, ApiNamespace<ApiSpecOptions>>);
}

private generateExecutorForApiSpecOptions<TR extends ApiSpecOptions>(
Expand All @@ -70,34 +68,37 @@ class StandardSDKBase<T extends ApiSpecs> {

private buildOperationHandlerForApiSpec(
executor: OperationExecutor,
defaultConfiguration?: OpenApiClientConfiguration,
): (
target: ApiOperationNamespace<ApiSpecOptions>,
target: ApiNamespace<ApiSpecOptions>,
operation: string,
) => OperationHandler {
return (
target: ApiOperationNamespace<ApiSpecOptions>,
target: ApiNamespace<ApiSpecOptions>,
operation: string,
): OperationHandler =>
async(
): OperationHandler => {
if (Object.getOwnPropertyNames(BaseApiNamespace.prototype).includes(operation)) {
return target[operation];
}
return async(
args?: Record<string, any>,
configuration?: Record<string, any>,
options?: Record<string, any>,
): Promise<Record<string, any>> =>
await executor.executeOperation(
operation,
args,
configuration,
{ ...defaultConfiguration, ...options },
{ ...target.defaultConfiguration, ...configuration },
{ ...target.defaultOptions, ...options },
);
};
}
}

type NamespacedApiOperationNamespace<T extends ApiSpecs> = {
[key in keyof T]: ApiOperationNamespace<T[key]>
type NamespacedApiNamespace<T extends ApiSpecs> = {
[key in keyof T]: ApiNamespace<T[key]>
};

export type StandardSDK<T extends ApiSpecs> = StandardSDKBase<T> & NamespacedApiOperationNamespace<T>;
export type StandardSDK<T extends ApiSpecs> = StandardSDKBase<T> & NamespacedApiNamespace<T>;

// eslint-disable-next-line @typescript-eslint/naming-convention
export const StandardSDK = {
Expand Down
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from '@comake/openapi-operation-executor/dist/OpenApiSchemaConfiguratio

// OpenAPI Types
export * from './openapi-types/OpenApiArgTypes';
export * from './openapi-types/OpenApiNamespace';
export * from './openapi-types/OpenApiOperationNamespace';
export * from './openapi-types/OpenApiOperationType';
export * from './openapi-types/OpenApiSpecOptions';
Expand All @@ -13,8 +14,9 @@ export * from './openapi-types/OpenApiSpecOptions';
export * from './operation-executor/OpenApiOperationExecutor';
export * from './operation-executor/OperationExecutor';

export * from './ApiOperationNamespace';
export * from './ApiNamespace';
export * from './ApiSpecOptions';
export * from './BaseApiNamespace';
export * from './BaseApiSpecOptions';
export * from './OperationHandler';
export * from './StandardSdk';
2 changes: 1 addition & 1 deletion src/openapi-types/OpenApiArgTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,4 @@ export type OpenApiArgTypes<
TSpec extends OpenApi,
TOperation extends string = string,
TParams = ArgsOfOpenApiOperation<TSpec, TOperation>
> = [TParams] extends [never] ? undefined : TParams;
> = [TParams] extends [never] ? never : TParams;
Loading

0 comments on commit bfae590

Please sign in to comment.