Skip to content

Commit

Permalink
Merge pull request #926 from lowdefy/requests
Browse files Browse the repository at this point in the history
Add V4 requests
  • Loading branch information
Gervwyk committed Nov 3, 2021
2 parents efb7f04 + 80c00f4 commit 149dba2
Show file tree
Hide file tree
Showing 104 changed files with 1,930 additions and 575 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.yaml
Expand Up @@ -39,4 +39,4 @@ rules:
jsx-a11y/no-static-element-interactions: 'off'
parserOptions:
sourceType: module
ecmaVersion: 9
ecmaVersion: latest
2 changes: 1 addition & 1 deletion .github/workflows/test-pulls.yml
Expand Up @@ -20,7 +20,7 @@ jobs:

# format tests don't pass on node 12 since icu is missing and tests don't work with locales
- name: Test packages
run: yarn test --ignore='@lowdefy/format' --ignore='@lowdefy/blocks-*'
run: yarn test --ignore='@lowdefy/format' --ignore='@lowdefy/blocks-*' --ignore='@lowdefy/graphql'

- name: Upload coverage to codecov
run: bash <(curl -s https://codecov.io/bash)
Expand Down
249 changes: 203 additions & 46 deletions .pnp.cjs

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion .yarn/sdks/eslint/package.json
@@ -1,6 +1,6 @@
{
"name": "eslint",
"version": "7.27.0-sdk",
"version": "8.1.0-sdk",
"main": "./lib/api.js",
"type": "commonjs"
}
2 changes: 1 addition & 1 deletion .yarn/sdks/prettier/package.json
@@ -1,6 +1,6 @@
{
"name": "prettier",
"version": "2.3.0-sdk",
"version": "2.4.1-sdk",
"main": "./index.js",
"type": "commonjs"
}
1 change: 1 addition & 0 deletions lerna.json
Expand Up @@ -3,6 +3,7 @@
"packages": [
"src/packages/*",
"src/packages/blocks/*",
"src/packages/connections/*",
"src/packages/servers/*"
],
"npmClient": "yarn",
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -25,6 +25,7 @@
"workspaces": [
"packages/*",
"packages/blocks/*",
"packages/connections/*",
"packages/servers/*"
],
"scripts": {
Expand Down
3 changes: 2 additions & 1 deletion packages/api/package.json
Expand Up @@ -29,7 +29,8 @@
"dist/*"
],
"scripts": {
"build": "yarn webpack",
"babel": "babel src --out-dir dist",
"build": "yarn clean && yarn babel",
"clean": "rm -rf dist",
"prepare": "yarn build",
"test": "jest --coverage",
Expand Down
10 changes: 6 additions & 4 deletions packages/api/src/context/createContext.js
Expand Up @@ -18,14 +18,16 @@ import createAuthorize from './createAuthorize';
import createReadConfigFile from './readConfigFile';
import verifyAuthorizationHeader from './verifyAuthorizationHeader';

async function createContext({ configDirectory, getSecrets }) {
const readConfigFile = createReadConfigFile({ configDirectory });
const [config, secrets] = await Promise.all([readConfigFile('config.json'), getSecrets()]);
function contextFn({ headers, host, protocol, setHeader }) {
async function createContext({ buildDirectory, connections, secrets }) {
const readConfigFile = createReadConfigFile({ buildDirectory });
const config = await readConfigFile('config.json');
function contextFn({ headers, host, logger, protocol, setHeader }) {
const context = {
config,
connections,
headers,
host,
logger,
protocol,
readConfigFile,
secrets,
Expand Down
27 changes: 20 additions & 7 deletions packages/api/src/context/createContext.test.js
Expand Up @@ -23,13 +23,15 @@ jest.mock('./createAuthorize');
jest.mock('./readConfigFile');
jest.mock('./verifyAuthorizationHeader');

const getSecrets = jest.fn();

getSecrets.mockImplementation(() => ({ secret: true }));
const connections = { Connection: true };
const secrets = { secret: true };

createAuthorize.mockImplementation(({ authenticated, roles = [] }) => ({ authenticated, roles }));

createReadConfigFile.mockImplementation(({ configDirectory }) => () => ({ configDirectory }));
createReadConfigFile.mockImplementation(({ buildDirectory }) => (path) => ({
buildDirectory,
path,
}));

verifyAuthorizationHeader.mockImplementation(() => ({
authenticated: true,
Expand All @@ -38,10 +40,11 @@ verifyAuthorizationHeader.mockImplementation(() => ({
}));

test('createContext', async () => {
const contextFn = await createContext({ configDirectory: 'configDirectory', getSecrets });
const contextFn = await createContext({ connections, buildDirectory: 'buildDirectory', secrets });
const context = contextFn({
headers: { header: 'header' },
host: 'host',
logger: 'logger',
protocol: 'https',
setHeader: 'setHeaderFunction',
});
Expand All @@ -55,12 +58,17 @@ test('createContext', async () => {
],
},
"config": Object {
"configDirectory": "configDirectory",
"buildDirectory": "buildDirectory",
"path": "config.json",
},
"connections": Object {
"Connection": true,
},
"headers": Object {
"header": "header",
},
"host": "host",
"logger": "logger",
"protocol": "https",
"readConfigFile": [Function],
"secrets": Object {
Expand All @@ -84,12 +92,17 @@ test('createContext', async () => {
],
},
"config": Object {
"configDirectory": "configDirectory",
"buildDirectory": "buildDirectory",
"path": "config.json",
},
"connections": Object {
"Connection": true,
},
"headers": Object {
"header": "header",
},
"host": "host",
"logger": "logger",
"protocol": "https",
"readConfigFile": [Function],
"secrets": Object {
Expand Down
4 changes: 2 additions & 2 deletions packages/api/src/context/readConfigFile.js
Expand Up @@ -17,9 +17,9 @@
import path from 'path';
import { cachedPromises, getFileExtension, readFile } from '@lowdefy/node-utils';

function createReadConfigFile({ configDirectory }) {
function createReadConfigFile({ buildDirectory }) {
async function readConfigFile(filePath) {
const fileContent = await readFile(path.resolve(configDirectory, filePath));
const fileContent = await readFile(path.resolve(buildDirectory, filePath));
if (getFileExtension(filePath) === 'json') {
return JSON.parse(fileContent);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/api/src/index.js
Expand Up @@ -22,6 +22,7 @@ import openIdLogoutUrl from './routes/auth/openIdLogoutUrl';
import pageConfig from './routes/page/pageConfig';
import pageHtml from './routes/page/pageHtml';
import rootConfig from './routes/rootConfig/rootConfig';
import request from './routes/request/request';

import {
AuthenticationError,
Expand All @@ -40,6 +41,7 @@ export {
pageConfig,
pageHtml,
rootConfig,
request,
AuthenticationError,
ConfigurationError,
RequestError,
Expand Down
28 changes: 28 additions & 0 deletions packages/api/src/routes/request/authorizeRequest.js
@@ -0,0 +1,28 @@
/*
Copyright 2020-2021 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import { ConfigurationError } from '../../context/errors';

function authorizeRequest({ authorize, logger }, { requestConfig }) {
if (!authorize(requestConfig)) {
logger.warn({ authorized: false }, 'Unauthorized Request');
// Throw does not exist error to avoid leaking information that request exists to unauthorized users
throw new ConfigurationError(`Request "${requestConfig.requestId}" does not exist.`);
}
logger.debug({ authorized: true }, 'Authorize Request');
}

export default authorizeRequest;
39 changes: 39 additions & 0 deletions packages/api/src/routes/request/callRequestResolver.js
@@ -0,0 +1,39 @@
/*
Copyright 2020-2021 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import { RequestError } from '../../context/errors';

async function callRequestResolver(
{ logger },
{ connectionProperties, requestConfig, requestProperties, requestHandler }
) {
try {
const response = await requestHandler.resolver({
request: requestProperties,
connection: connectionProperties,
});
return response;
} catch (error) {
const err = new RequestError(error.message);
logger.debug(
{ params: { id: requestConfig.requestId, type: requestConfig.type }, err },
err.message
);
throw err;
}
}

export default callRequestResolver;
37 changes: 37 additions & 0 deletions packages/api/src/routes/request/checkConnectionRead.js
@@ -0,0 +1,37 @@
/*
Copyright 2020-2021 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { ConfigurationError } from '../../context/errors';

function checkConnectionRead(
{ logger },
{ connectionConfig, connectionProperties, requestConfig, requestHandler }
) {
if (requestHandler.checkRead && connectionProperties.read === false) {
const err = new ConfigurationError(
`Connection "${connectionConfig.connectionId}" does not allow reads.`
);
logger.debug(
{
params: { connectionId: connectionConfig.connectionId, requestId: requestConfig.requestId },
err,
},
err.message
);
throw err;
}
}

export default checkConnectionRead;
37 changes: 37 additions & 0 deletions packages/api/src/routes/request/checkConnectionWrite.js
@@ -0,0 +1,37 @@
/*
Copyright 2020-2021 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { ConfigurationError } from '../../context/errors';

function checkConnectionWrite(
{ logger },
{ connectionConfig, connectionProperties, requestConfig, requestHandler }
) {
if (requestHandler.checkWrite && connectionProperties.write !== true) {
const err = new ConfigurationError(
`Connection "${connectionConfig.connectionId}" does not allow writes.`
);
logger.debug(
{
params: { connectionId: connectionConfig.connectionId, requestId: requestConfig.requestId },
err,
},
err.message
);
throw err;
}
}

export default checkConnectionWrite;
50 changes: 50 additions & 0 deletions packages/api/src/routes/request/evaluateOperators.js
@@ -0,0 +1,50 @@
/*
Copyright 2020-2021 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import { NodeParser } from '@lowdefy/operators';

import { RequestError } from '../../context/errors';

async function evaluateOperators({ secrets, user }, { connectionConfig, payload, requestConfig }) {
const operatorsParser = new NodeParser({
payload,
secrets,
user,
});
await operatorsParser.init();
const { output: connectionProperties, errors: connectionErrors } = operatorsParser.parse({
input: connectionConfig.properties || {},
location: connectionConfig.connectionId,
});
if (connectionErrors.length > 0) {
throw new RequestError(connectionErrors[0]);
}

const { output: requestProperties, errors: requestErrors } = operatorsParser.parse({
input: requestConfig.properties || {},
location: requestConfig.requestId,
});
if (requestErrors.length > 0) {
throw new RequestError(requestErrors[0]);
}

return {
connectionProperties,
requestProperties,
};
}

export default evaluateOperators;

0 comments on commit 149dba2

Please sign in to comment.