feat(build): add build pages function

This commit is contained in:
Sam Tolmay 2020-10-20 17:00:15 +02:00
parent ac216d4483
commit 01e892fad5
5 changed files with 1708 additions and 12 deletions

View File

@ -0,0 +1,202 @@
/* eslint-disable no-param-reassign */
/*
Copyright 2020 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 { set, type } from '@lowdefy/helpers';
const blockTypes = {};
/* Page and block build steps
Pages:
- set pageId = id
- set id = `page:${page.id}`
Blocks:
- set blockId = id
- set id = `block:${pageId}:${block.id}` if not a page
- set request ids
- set mutation ids
- set block meta
- set blocks to areas.content
*/
function buildRequests(block, context) {
if (!type.isNone(block.requests)) {
if (type.isArray(block.requests)) {
block.requests.forEach((request) => {
request.requestId = request.id;
request.id = `request:${context.pageId}:${context.contextId}:${request.id}`;
context.requests.push(request);
});
delete block.requests;
} else {
throw new Error(
`Requests is not an array at ${block.blockId} on page ${
context.pageId
}. Received ${JSON.stringify(block.requests)}`
);
}
}
if (!type.isNone(block.mutations)) {
if (type.isArray(block.mutations)) {
block.mutations.forEach((mutation) => {
mutation.mutationId = mutation.id;
mutation.id = `mutation:${context.pageId}:${context.contextId}:${mutation.id}`;
context.mutations.push(mutation);
});
delete block.mutations;
} else {
throw new Error(
`Mutations is not an array at ${block.blockId} on page ${
context.pageId
}. Received ${JSON.stringify(block.mutations)}`
);
}
}
}
async function checkPageIsContext(page, metaLoader) {
if (type.isNone(page.type)) {
throw new Error(`Page type is not defined at ${page.pageId}.`);
}
if (!type.isString(page.type)) {
throw new Error(
`Page type is not a string at ${page.pageId}. Received ${JSON.stringify(page.type)}`
);
}
const meta = await metaLoader.load(page.type);
if (!meta) {
throw new Error(
`Invalid block type at page ${page.pageId}. Received ${JSON.stringify(page.type)}`
);
}
if (meta.category !== 'context') {
throw new Error(
`Page ${page.pageId} is not of category "context". Received ${JSON.stringify(page.type)}`
);
}
}
async function setBlockMeta(block, context) {
if (type.isNone(block.type)) {
throw new Error(`Block type is not defined at ${block.blockId} on page ${context.pageId}.`);
}
if (!type.isString(block.type)) {
throw new Error(
`Block type is not a string at ${block.blockId} on page ${
context.pageId
}. Received ${JSON.stringify(block.type)}`
);
}
const meta = await context.metaLoader.load(block.type);
if (!meta) {
throw new Error(
`Invalid Block type at ${block.blockId} on page ${context.pageId}. Received ${JSON.stringify(
block.type
)}`
);
}
const { category, loading, module, scope, url, valueType } = meta;
block.meta = { category, loading, module, scope, url };
if (category === 'input') {
block.meta.valueType = valueType;
}
if (category === 'list') {
// include valueType to ensure block has value on init
block.meta.valueType = 'array';
}
}
async function buildBlock(block, context) {
if (!type.isObject(block)) {
throw new Error(
`Expected block to be an object on ${context.pageId}. Received ${JSON.stringify(block)}`
);
}
if (type.isUndefined(block.id)) {
throw new Error(`Block id missing at page ${context.pageId}`);
}
block.blockId = block.id;
block.id = `block:${context.pageId}:${block.id}`;
await setBlockMeta(block, context);
if (block.meta.category === 'context') {
context.requests = [];
context.mutations = [];
context.contextId = block.blockId;
}
buildRequests(block, context);
if (block.meta.category === 'context') {
block.requests = context.requests;
block.mutations = context.mutations;
}
if (!type.isNone(block.blocks)) {
if (type.isArray(block.blocks)) {
set(block, 'areas.content.blocks', block.blocks);
delete block.blocks;
} else {
throw new Error(
`Blocks at ${block.blockId} on page ${
context.pageId
} is not an array. Received ${JSON.stringify(block.blocks)}`
);
}
}
if (type.isObject(block.areas)) {
let promises = [];
Object.keys(block.areas).forEach((key) => {
if (type.isArray(block.areas[key].blocks)) {
const blockPromises = block.areas[key].blocks.map(async (blk) => {
await buildBlock(blk, context);
});
promises = promises.concat(blockPromises);
} else {
throw new Error(
`Expected blocks to be an array at ${block.blockId} in area ${key} on page ${
context.pageId
}. Received ${JSON.stringify(block.areas[key].blocks)}`
);
}
});
await Promise.all(promises);
}
}
async function buildPages({ components, context }) {
const pages = type.isArray(components.pages) ? components.pages : [];
const pageBuildPromises = pages.map(async (page, i) => {
if (type.isUndefined(page.id)) {
throw new Error(`Page id missing at page ${i}`);
}
page.pageId = page.id;
await checkPageIsContext(page, context.metaLoader);
await buildBlock(page, {
pageId: page.pageId,
requests: [],
mutations: [],
metaLoader: context.metaLoader,
});
// set page.id since buildBlock sets id as well.
page.id = `page:${page.pageId}`;
});
await Promise.all(pageBuildPromises);
context.logger.debug('Built pages');
return components;
}
export default buildPages;

File diff suppressed because it is too large Load Diff

View File

@ -17,13 +17,12 @@
import buildRefs from './buildRefs';
import testContext from '../test/testContext';
// TODO mock loader should throw if file not found
const configLoaderMockImplementation = (files) => {
const mockImp = (filePath) => {
const file = files.find((file) => file.path === filePath);
if (!file) {
throw new Error(
`Tried to read file with file path ${JSON.stringify(filePath)}, but file does not exist`
`Tried to read file with file path ${JSON.stringify(filePath)}, but file does not exist.`
);
}
return file.content;
@ -49,7 +48,9 @@ test('buildRefs file not found', async () => {
},
];
mockConfigLoader.mockImplementation(configLoaderMockImplementation(files));
await expect(buildRefs({ context })).rejects.toThrow('File "doesNotExist" not found.');
await expect(buildRefs({ context })).rejects.toThrow(
'Tried to read file with file path "doesNotExist", but file does not exist.'
);
});
test('buildRefs', async () => {

View File

@ -29,10 +29,7 @@ const context = testContext({ logger });
beforeEach(() => {
mockLogWarn.mockReset();
});
beforeEach(() => {
mockLogWarn.mockReset();
mockLogSuccess.mockReset();
});
test('empty components', async () => {

View File

@ -1,4 +1,4 @@
function testContext({ logger = {}, configLoader, artifactSetter } = {}) {
function testContext({ artifactSetter, configLoader, logger = {}, metaLoader } = {}) {
const defaultLogger = {
success: () => {},
warn: () => {},
@ -7,11 +7,14 @@ function testContext({ logger = {}, configLoader, artifactSetter } = {}) {
};
const context = {
artifactSetter: {
set: () => [],
},
configLoader: {
load: () => {},
},
artifactSetter: {
set: () => [],
metaLoader: {
load: () => {},
},
};
@ -20,11 +23,14 @@ function testContext({ logger = {}, configLoader, artifactSetter } = {}) {
...logger,
};
if (artifactSetter) {
context.artifactSetter = artifactSetter;
}
if (configLoader) {
context.configLoader = configLoader;
}
if (artifactSetter) {
context.artifactSetter = artifactSetter;
if (metaLoader) {
context.metaLoader = metaLoader;
}
return context;