Skip to content

Commit

Permalink
Merge pull request #185 from superfaceai/dev
Browse files Browse the repository at this point in the history
Release 1.0.1
  • Loading branch information
lukas-valenta authored Nov 24, 2021
2 parents a948565 + b8e17b9 commit aafe336
Show file tree
Hide file tree
Showing 14 changed files with 631 additions and 753 deletions.
10 changes: 9 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,21 @@ jobs:
RELEASE_LEVEL: ${{ github.event.inputs.release-level }}

- name: Bump pre-release version
if: startsWith(github.event.inputs.release-level, 'pre')
if: startsWith(github.event.inputs.release-level, 'pre') && github.ref_name != 'dev'
run: |
echo "NEW_VERSION=$(npm --no-git-tag-version --preid=beta version $RELEASE_LEVEL)" >> $GITHUB_ENV
echo "RELEASE_TAG=beta" >> $GITHUB_ENV
env:
RELEASE_LEVEL: ${{ github.event.inputs.release-level }}

- name: Bump rc pre-release version
if: startsWith(github.event.inputs.release-level, 'pre') && github.ref_name == 'dev'
run: |
echo "NEW_VERSION=$(npm --no-git-tag-version --preid=rc version $RELEASE_LEVEL)" >> $GITHUB_ENV
echo "RELEASE_TAG=next" >> $GITHUB_ENV
env:
RELEASE_LEVEL: ${{ github.event.inputs.release-level }}

# Update changelog unreleased section with new version
- name: Update changelog
uses: superfaceai/release-changelog-action@v1
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- Use [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901) (arrays not supported) to specify api token location in body

### Fixed
- ProfileParameterValidator now resolves named models before field references
- Buffer serialization in request body
- `undefined` values are removed when stringifying records


## [1.0.0] - 2021-11-04
### Added
Expand Down
26 changes: 1 addition & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ yarn add @superfaceai/one-sdk

### Using the OneSDK

To interact with Superface, initialize a new Superface OneSDK instance, references the profile and use case you're wanting to use, then perform it to get the result. You can use either the typed or untyped interface for the `SuperfaceClient`.
To interact with Superface, initialize a new Superface OneSDK instance, references the profile and use case you're wanting to use, then perform it to get the result.

#### Initializing the OneSDK client

Expand Down Expand Up @@ -91,30 +91,6 @@ const result = await profile.getUsecase('<usecaseName>').perform(
);
```

### Using the Typed OneSDK

You can also use generated typed client, which is very similar:

Make sure a profile is installed with types by running `superface install --types <profileName>[@<profileVersion>]` in the project directory.

```typescript
// This should point to superface directory in project root
// instead of @superfaceai/one-sdk
import { SuperfaceClient } from 'superface/sdk';

const client = new SuperfaceClient();
// This client should now autocomplete your installed profileVersion
const profile = await client.getProfile('myProfile');
const result = await profile.useCases.myUseCase.perform(
{
inputField: 1,
anotherInputField: 'hello',
},
// optional, if provider is missing selects first configured provider from super.json
{ provider }
);
```

### Handling the results from `perform`

The `perform` method will take your inputs and additional information and perform the use case asynchronously. This method always returns a Result type that is either `Ok` or `Err`. This follows the [neverthrow](https://github.com/supermacro/neverthrow) approach. The SDK provides multiple ways to work with result.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@superfaceai/one-sdk",
"version": "1.0.0",
"version": "1.0.1-rc.1",
"description": "Level 5 autonomous, self-driving API client, https://superface.ai",
"main": "dist/index.js",
"source": "src/index.ts",
Expand Down
9 changes: 6 additions & 3 deletions src/internal/errors.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,10 +268,13 @@ export function missingSecurityValuesError(id: string): SDKExecutionError {
);
}

export function apiKeyInBodyError(bodyType: string): SDKExecutionError {
export function apiKeyInBodyError(
valueLocation: string,
bodyType: string
): SDKExecutionError {
return new SDKExecutionError(
'ApiKey in body can be used only when body is an object.',
[`Actual body type is ${bodyType}`],
'ApiKey in body can be used only on object.',
[`Actual ${valueLocation} is ${bodyType}`],
[]
);
}
Expand Down
103 changes: 103 additions & 0 deletions src/internal/interpreter/http/security.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { ApiKeyPlacement, SecurityType } from '@superfaceai/ast';

import { SDKExecutionError } from '../../errors';
import {
applyApiKeyAuth,
RequestContext,
SecurityConfiguration,
} from './security';

describe('http · security', () => {
describe('#applyApiKeyAuth', () => {
describe('in body', () => {
let context: RequestContext;
let configuration: SecurityConfiguration & { type: SecurityType.APIKEY };

beforeEach(() => {
context = {
headers: {},
pathParameters: {},
queryAuth: {},
requestBody: undefined,
};
configuration = {
id: 'test',
type: SecurityType.APIKEY,
in: ApiKeyPlacement.BODY,
name: undefined,
apikey: 'secret',
};
});

it('sets name with Primitive type', () => {
configuration.name = 'token';
applyApiKeyAuth(context, configuration);

expect(context.requestBody).toEqual({ token: 'secret' });
});

it('creates new nested sctructure', () => {
configuration.name = '/a/b/c';
applyApiKeyAuth(context, configuration);

expect(context.requestBody).toEqual({
a: {
b: {
c: 'secret',
},
},
});
});

it('keep content of existing objects', () => {
context.requestBody = { d: 'existing' };
configuration.name = '/a/b/c';
applyApiKeyAuth(context, configuration);

expect(context.requestBody).toEqual({
a: {
b: {
c: 'secret',
},
},
d: 'existing',
});
});

it('throws exception if request body is array', () => {
context.requestBody = [];
expect(() => applyApiKeyAuth(context, configuration)).toThrowError(
new SDKExecutionError(
'ApiKey in body can be used only on object.',
['Actual body is Array'],
[]
)
);
});

it('throws exception if in body path is array', () => {
context.requestBody = { a: { b: [] } };
configuration.name = '/a/b/c';
expect(() => applyApiKeyAuth(context, configuration)).toThrowError(
new SDKExecutionError(
'ApiKey in body can be used only on object.',
['Actual value at /a/b is Array'],
[]
)
);
});

it('throws exception if Primitive value is in body path', () => {
context.requestBody = { a: { b: { c: 'xxx' } } };
configuration.name = '/a/b/c';
expect(() => applyApiKeyAuth(context, configuration)).toThrowError(
new SDKExecutionError(
'ApiKey in body can be used only on object.',
['Actual value at /a/b/c is string'],
[]
)
);
});
});
});
});
45 changes: 34 additions & 11 deletions src/internal/interpreter/http/security.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,35 @@ export type RequestContext = {
requestBody: Variables | undefined;
};

export function applyApiKeyAuthInBody(
requestBody: Variables,
referenceTokens: string[],
apikey: string,
visitedReferenceTokens: string[] = []
): Variables {
if (typeof requestBody !== 'object' || Array.isArray(requestBody)) {
const valueLocation = visitedReferenceTokens.length
? `value at /${visitedReferenceTokens.join('/')}`
: 'body';
const bodyType = Array.isArray(requestBody) ? 'Array' : typeof requestBody;

throw apiKeyInBodyError(valueLocation, bodyType);
}

const token = referenceTokens.shift();
if (token === undefined) {
return apikey;
}

const segVal = requestBody[token] ?? {};
requestBody[token] = applyApiKeyAuthInBody(segVal, referenceTokens, apikey, [
...visitedReferenceTokens,
token,
]);

return requestBody;
}

export function applyApiKeyAuth(
context: RequestContext,
configuration: SecurityConfiguration & { type: SecurityType.APIKEY }
Expand All @@ -45,17 +74,11 @@ export function applyApiKeyAuth(
break;

case ApiKeyPlacement.BODY:
if (
typeof context.requestBody !== 'object' ||
Array.isArray(context.requestBody)
) {
throw apiKeyInBodyError(
Array.isArray(context.requestBody)
? 'Array'
: typeof context.requestBody
);
}
context.requestBody[name] = configuration.apikey;
context.requestBody = applyApiKeyAuthInBody(
context.requestBody ?? {},
name.startsWith('/') ? name.slice(1).split('/') : [name],
configuration.apikey
);
break;

case ApiKeyPlacement.PATH:
Expand Down
2 changes: 0 additions & 2 deletions src/internal/interpreter/map-interpreter.errors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -632,8 +632,6 @@ AST Path: definitions[0].statements[0].assignments[0].value`
);

const result = await interpreter.perform(ast);
console.log(result);

expect(result.isErr() && result.error.toString()).toMatch(
'the best error in the world'
);
Expand Down
Loading

0 comments on commit aafe336

Please sign in to comment.