Skip to content

Commit

Permalink
Refined injectors to be fully async
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianRappl committed Nov 20, 2023
1 parent 64abc07 commit 7c95a89
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 67 deletions.
8 changes: 4 additions & 4 deletions src/tooling/piral-cli/src/injectors/pilet-injector.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ describe('Piral-CLI pilet injector', () => {
expect(injector.active).toBeFalsy();
});

it('PiletInjector does not send empty content', () => {
it('PiletInjector does not send empty content', async () => {
// Arrange
const core = new EventEmitter();
const injector = new PiletInjector(optionsMock, configMock, core);
Expand All @@ -51,7 +51,7 @@ describe('Piral-CLI pilet injector', () => {

// Act
try {
injector.sendFile(target, url); // this file does not exist
await injector.sendFile(target, url); // this file does not exist
} catch {
hasFailed = true;
}
Expand Down Expand Up @@ -113,7 +113,7 @@ describe('Piral-CLI pilet injector', () => {
expect(res).toBeUndefined();
});

it('PiletInjector wont crash on request with no target', () => {
it('PiletInjector wont crash on request with no target', async () => {
// Arrange
const optionsMock = {
pilets: [],
Expand All @@ -136,7 +136,7 @@ describe('Piral-CLI pilet injector', () => {
} as KrasRequest;

// Act
const res = injector.handle(request);
const res = await injector.handle(request);

// Assert
expect(res).toBeUndefined();
Expand Down
90 changes: 49 additions & 41 deletions src/tooling/piral-cli/src/injectors/pilet-injector.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import { URL } from 'url';
import { join } from 'path';
import { EventEmitter } from 'events';
import { readFileSync, existsSync, statSync, writeFileSync } from 'fs';
import { KrasInjector, KrasResponse, KrasRequest, KrasInjectorConfig, KrasConfiguration, KrasResult } from 'kras';
import { readFile, stat, writeFile } from 'fs/promises';
import { KrasInjector, KrasRequest, KrasInjectorConfig, KrasConfiguration, KrasResult } from 'kras';
import { log } from '../common/log';
import { getPiletSpecMeta } from '../common/spec';
import { config as commonConfig } from '../common/config';
import { axios, mime, jju } from '../external';
import { Bundler } from '../types';

interface Pilet {
bundler: Bundler;
root: string;
getMeta(basePath: string): PiletMetadata;
}

export interface PiletInjectorConfig extends KrasInjectorConfig {
/**
* The pilets to serve.
Expand Down Expand Up @@ -54,31 +48,43 @@ export interface PiletInjectorConfig extends KrasInjectorConfig {
headers?: Record<string, string>;
}

interface Pilet {
bundler: Bundler;
root: string;
getMeta(basePath: string): PiletMetadata;
}

interface PiletMetadata {
name?: string;
config?: Record<string, any>;
[key: string]: unknown;
}

function getMetaOverride(root: string, metaFile: string) {
async function getMetaOverride(root: string, metaFile: string) {
if (metaFile) {
const metaPath = join(root, metaFile);
const exists = await stat(metaPath).then(
() => true,
() => false,
);

if (existsSync(metaPath)) {
return jju.parse(readFileSync(metaPath, 'utf8'));
if (exists) {
const metaContent = await readFile(metaPath, 'utf8');
return jju.parse(metaContent);
}
}

return undefined;
}

function fillPiletMeta(pilet: Pilet, metaFile: string, subPath: string) {
async function fillPiletMeta(pilet: Pilet, metaFile: string, subPath: string) {
const { root, bundler } = pilet;
const packagePath = join(root, 'package.json');
const def = jju.parse(readFileSync(packagePath, 'utf8'));
const jsonContent = await readFile(packagePath, 'utf8');
const def = jju.parse(jsonContent);
const file = bundler.bundle.name.replace(/^[\/\\]/, '');
const target = join(bundler.bundle.dir, file);
const metaOverride = getMetaOverride(root, metaFile);
const metaOverride = await getMetaOverride(root, metaFile);

pilet.getMeta = (parentPath) => {
const basePath = `${parentPath}${subPath}`;
Expand All @@ -96,11 +102,11 @@ function fillPiletMeta(pilet: Pilet, metaFile: string, subPath: string) {
};
}

type FeedResponse = { items?: Array<PiletMetadata> } | Array<PiletMetadata> | PiletMetadata;

async function loadFeed(feed: string) {
try {
const response = await axios.default.get<{ items?: Array<PiletMetadata> } | Array<PiletMetadata> | PiletMetadata>(
feed,
);
const response = await axios.default.get<FeedResponse>(feed);

if (Array.isArray(response.data)) {
return response.data;
Expand Down Expand Up @@ -161,8 +167,8 @@ export default class PiletInjector implements KrasInjector {
});

pilets.forEach((p, i) =>
p.bundler.on(() => {
fillPiletMeta(p, config.meta, `/${i}/`);
p.bundler.on(async () => {
await fillPiletMeta(p, config.meta, `/${i}/`);

for (const id of Object.keys(cbs)) {
const { baseUrl, notify } = cbs[id];
Expand Down Expand Up @@ -268,7 +274,7 @@ export default class PiletInjector implements KrasInjector {
return merged;
}

sendContent(content: Buffer | string, type: string, url: string): KrasResponse {
sendContent(content: Buffer | string, type: string, url: string): KrasResult {
const { headers } = this.config;

return {
Expand All @@ -286,8 +292,8 @@ export default class PiletInjector implements KrasInjector {
};
}

sendFile(target: string, url: string): KrasResponse {
const content = readFileSync(target);
async sendFile(target: string, url: string): Promise<KrasResult> {
const content = await readFile(target);
const type = mime.getType(target) ?? 'application/octet-stream';
return this.sendContent(content, type, url);
}
Expand All @@ -298,23 +304,25 @@ export default class PiletInjector implements KrasInjector {
const pilet = pilets[+index];
const bundler = pilet?.bundler;

await bundler?.ready();

if (!path) {
await bundler?.ready();
const content = await this.getIndexMeta(baseUrl);
return this.sendContent(content, 'application/json', url);
} else {
return bundler?.ready().then(() => {
const target = join(bundler.bundle.dir, rest.join('/'));
} else if (bundler?.bundle) {
const target = join(bundler.bundle.dir, rest.join('/'));
const info = await stat(target).catch(() => undefined);

if (existsSync(target) && statSync(target).isFile()) {
return this.sendFile(target, url);
}
});
if (info && info.isFile()) {
return await this.sendFile(target, url);
}
}

return undefined;
}

sendIndexFile(target: string, url: string, baseUrl: string): KrasResponse {
const indexHtml = readFileSync(target, 'utf8');
async sendIndexFile(target: string, url: string, baseUrl: string): Promise<KrasResult> {
const indexHtml = await readFile(target, 'utf8');

// mechanism to inject server side debug piletApi config into piral emulator
const windowInjectionScript = `window['dbg:pilet-api'] = '${this.getPiletApi(baseUrl)}';`;
Expand All @@ -331,17 +339,17 @@ export default class PiletInjector implements KrasInjector {
return false;
}

const fileInfo = statSync(target, { throwIfNoEntry: false });
const fileInfo = await stat(target).catch(() => undefined);

if (!fileInfo || fileInfo.mtime < this.proxyInfo.date) {
const url = new URL(path, this.proxyInfo.source);
const response = await axios.default.get(url.href, { responseType: 'arraybuffer' });
writeFileSync(target, response.data);
await writeFile(target, response.data);
}

return true;
} else {
const fileInfo = statSync(target, { throwIfNoEntry: false });
const fileInfo = await stat(target).catch(() => undefined);
return fileInfo && fileInfo.isFile();
}
}
Expand All @@ -353,22 +361,22 @@ export default class PiletInjector implements KrasInjector {

if (!req.target) {
if (req.url.startsWith(publicUrl)) {
const path = req.url.substring(publicUrl.length).split('?')[0];
const path = req.url.substring(publicUrl.length).split('?').shift();

if (app) {
const target = join(app, path);

if (await this.shouldLoad(target, path)) {
if (req.url === this.indexPath) {
return this.sendIndexFile(target, req.url, baseUrl);
return await this.sendIndexFile(target, req.url, baseUrl);
}

return this.sendFile(target, req.url);
return await this.sendFile(target, req.url);
}
}

if (req.url !== this.indexPath) {
return this.handle({
return await this.handle({
...req,
url: this.indexPath,
});
Expand All @@ -377,8 +385,8 @@ export default class PiletInjector implements KrasInjector {

return undefined;
} else if (req.target === api) {
const path = req.url.substring(1).split('?')[0];
return this.sendResponse(path, req.url, baseUrl);
const path = req.url.substring(1).split('?').shift();
return await this.sendResponse(path, req.url, baseUrl);
}
}
}
4 changes: 2 additions & 2 deletions src/tooling/piral-cli/src/injectors/piral-injector.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe('Piral-CLI piral injector', () => {
expect(injector.active).toBeFalsy();
});

it('PiralInjector can send reponse and fails with invalid path', () => {
it('PiralInjector can send reponse and fails with invalid path', async () => {
// Arrange
const config = {
bundler: bundlerMock,
Expand All @@ -75,7 +75,7 @@ describe('Piral-CLI piral injector', () => {
const injector = new PiralInjector(config, undefined as any, new EventEmitter());

// Act
const res = injector.sendResponse('some/nice/invalid/path', 'sometarget.file', 'someDir', 'localhost:1234');
const res = await injector.sendResponse('some/nice/invalid/path', 'sometarget.file', 'someDir', 'localhost:1234');

// Assert
expect(res).toBeUndefined();
Expand Down
51 changes: 31 additions & 20 deletions src/tooling/piral-cli/src/injectors/piral-injector.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
import { join } from 'path';
import { EventEmitter } from 'events';
import { readFileSync, existsSync, statSync } from 'fs';
import { KrasInjector, KrasResponse, KrasRequest, KrasInjectorConfig, KrasConfiguration } from 'kras';
import { readFile, stat } from 'fs/promises';
import { KrasInjector, KrasRequest, KrasInjectorConfig, KrasConfiguration, KrasResult } from 'kras';
import { mime } from '../external';
import { Bundler } from '../types';

/**
* The maximum amount of retries when sending a response
*/
const maxRetrySendResponse = 4;

export interface PiralInjectorConfig extends KrasInjectorConfig {
bundler: Bundler;
publicUrl: string;
feed?: string;
headers?: Record<string, string>;
}

/**
* The maximum amount of retries when sending a response
*/
const maxRetrySendResponse = 4;

async function isNoFile(target: string) {
try {
const info = await stat(target);
return !info.isFile();
} catch {
return true;
}
}

export default class PiralInjector implements KrasInjector {
public config: PiralInjectorConfig;

Expand Down Expand Up @@ -63,7 +72,7 @@ export default class PiralInjector implements KrasInjector {

setOptions() {}

sendContent(content: Buffer | string, type: string, url: string): KrasResponse {
sendContent(content: Buffer | string, type: string, url: string): KrasResult {
const { headers } = this.config;
return {
injector: { name: this.name },
Expand All @@ -80,8 +89,8 @@ export default class PiralInjector implements KrasInjector {
};
}

sendIndexFile(target: string, url: string): KrasResponse {
const indexHtml = readFileSync(target, 'utf8');
async sendIndexFile(target: string, url: string): Promise<KrasResult> {
const indexHtml = await readFile(target, 'utf8');
const { feed } = this.config;

if (feed) {
Expand All @@ -96,34 +105,36 @@ export default class PiralInjector implements KrasInjector {
return this.sendContent(indexHtml, mime.getType(target), url);
}

sendResponse(path: string, target: string, dir: string, url: string, recursionDepth = 0): KrasResponse {
async sendResponse(path: string, target: string, dir: string, url: string, recursionDepth = 0): Promise<KrasResult> {
if (recursionDepth > maxRetrySendResponse) {
return undefined;
}

const { bundler } = this.config;
const newTarget = join(bundler.bundle.dir, bundler.bundle.name);

if (!path || !existsSync(target) || !statSync(target).isFile()) {
return this.sendResponse(bundler.bundle.name, newTarget, dir, url, recursionDepth + 1);
if (!path || (await isNoFile(target))) {
return await this.sendResponse(bundler.bundle.name, newTarget, dir, url, recursionDepth + 1);
} else if (target === newTarget) {
return this.sendIndexFile(target, url);
return await this.sendIndexFile(target, url);
} else {
const type = mime.getType(target) ?? 'application/octet-stream';
const content = await readFile(target);
return this.sendContent(content, type, url);
}

const type = mime.getType(target) ?? 'application/octet-stream';
return this.sendContent(readFileSync(target), type, url);
}

handle(req: KrasRequest): KrasResponse {
async handle(req: KrasRequest): Promise<KrasResult> {
if (!req.target) {
const { bundler, publicUrl } = this.config;

if (req.url.startsWith(publicUrl) || `${req.url}/` === publicUrl) {
const pathLength = publicUrl.length || 1;
const path = req.url.substring(pathLength);
const dir = bundler.bundle.dir;
const target = join(dir, path.split('?')[0]);
return bundler.ready().then(() => this.sendResponse(path, target, dir, req.url));
const target = join(dir, path.split('?').shift());
await bundler.ready();
return await this.sendResponse(path, target, dir, req.url);
}
}
}
Expand Down

0 comments on commit 7c95a89

Please sign in to comment.