Skip to content

Commit

Permalink
feat: use env eval
Browse files Browse the repository at this point in the history
  • Loading branch information
belopash committed May 1, 2024
1 parent b2f0340 commit 14a3ac3
Showing 1 changed file with 122 additions and 20 deletions.
142 changes: 122 additions & 20 deletions src/manifest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Expression, Parser } from '@subsquid/manifest-expr';
import yaml from 'js-yaml';
import { defaultsDeep, get, isPlainObject, mapValues, set } from 'lodash';
import { cloneDeep, defaultsDeep, get, isPlainObject, mapValues, set } from 'lodash';

import { manifestSchema } from './schema';
import {
Expand All @@ -12,19 +13,19 @@ import {
export interface Manifest extends ManifestValue {}

export class Manifest {
constructor(value: Partial<ManifestValue> = {}) {
constructor(value: ManifestValue) {
defaultsDeep(this, value);

if (this.manifestVersion) {
value.manifest_version = value.manifestVersion;
delete value.manifestVersion;
this.manifest_version = this.manifestVersion;
delete this.manifestVersion;
}

// Duck typings and legacy manifests
if (this.deploy?.processor === null) {
this.deploy.processor = [];
} else if (this.deploy?.processor && isPlainObject(this.deploy?.processor)) {
const proc = value?.deploy?.processor as unknown as ManifestProcessor;
const proc = this.deploy?.processor as unknown as ManifestProcessor;
if (!proc.name) {
proc.name = 'processor';
}
Expand All @@ -38,7 +39,7 @@ export class Manifest {
}

if (this.scale) {
value = defaultsDeep(value, {
defaultsDeep(this, {
scale: {
dedicated: false,
},
Expand Down Expand Up @@ -110,19 +111,20 @@ export class Manifest {
}

values(): ManifestValue {
return defaultsDeep(
{},
{
manifest_version: this.manifest_version,
name: this.name,
description: this.description,
version: this.version,
build: this.build,
deploy: this.deploy,
scale: this.scale,
queries: this.queries,
},
);
return this.toObject();
}

toObject() {
return {
manifest_version: this.manifest_version,
name: this.name,
description: this.description,
version: this.version,
build: cloneDeep(this.build),
deploy: cloneDeep(this.deploy),
scale: cloneDeep(this.scale),
queries: cloneDeep(this.queries),
};
}

toString(pretty = false) {
Expand All @@ -133,6 +135,97 @@ export class Manifest {
return yaml.dump(this.values());
}

eval(context: Record<string, any>): ManifestValue {
const { error: parseError, value: parsed } = this.parse();
if (parseError) {
throw parseError;
}

const _eval = (env: Record<string, Expression> | undefined, path: string) => {
return mapValues(env, (value, key) => {
try {
return value.eval(context);
} catch (e) {
throw new ManifestEvaluatingError('Unable to evaluate manifest', [
getError(`${path}.${key}`, get(this as ManifestValue, `${path}.${key}`), e),
]);
}
});
};

const raw = this.toObject();

return {
...raw,
deploy: defaultsDeep(
{
env: _eval(parsed.env, 'deploy.env'),
init: { env: _eval(parsed.init.env, 'deploy.init.env') },
api: { env: _eval(parsed.api.env, 'deploy.api.env') },
processor: parsed.processor.map((p, index) =>
defaultsDeep(
{
env: _eval(p.env, `deploy.processor.[${index}].env`),
},
raw.deploy?.processor?.[index],
),
),
},
raw.deploy,
),
};
}

variables(path?: string[]) {
const res: Set<string> = new Set();

const { error: parseError, value: parsed } = this.parse();
if (parseError) {
throw parseError;
}

const _variables = (env: Record<string, Expression> | undefined) => {
mapValues(env, value => value.variables(path).forEach(v => res.add(v)));
};

_variables(parsed.env);
_variables(parsed.init.env);
_variables(parsed.api.env);
parsed.processor?.forEach(p => _variables(p.env));

return [...res];
}

private parse() {
const parser = new Parser();
const errors: string[] = [];

const _parse = (env: Record<string, string> | undefined, path: string) => {
return mapValues(env, (value, key) => {
try {
return parser.parse(value);
} catch (e: unknown) {
errors.push(getError(`${path}.${key}`, value, e));
return new Expression(['']);
}
});
};

const res = {
env: _parse(this.deploy?.env, 'deploy.env'),
init: { env: _parse(this.deploy?.init?.env, 'deploy.init.env') },
api: { env: _parse(this.deploy?.api?.env, 'deploy.api.env') },
processor:
this.deploy?.processor?.map((p, index) => ({
env: _parse(p.env, `deploy.processor.[${index}].env`),
})) || [],
};

return errors.length > 0
? { error: new ManifestEvaluatingError(`Unable to parse manifest`, errors) }
: { value: res };
}

static validate(value: Partial<ManifestValue>, options: ManifestOptions = {}) {
const res = manifestSchema.validate(value, {
allowUnknown: options.validation?.allowUnknown,
Expand Down Expand Up @@ -173,7 +266,7 @@ export class Manifest {
}

return {
value: new Manifest(res.value),
value: new Manifest(res.value!),
};
} catch (e: unknown) {
return {
Expand All @@ -197,3 +290,12 @@ function getError(path: string, expression: string | undefined, error: any) {
error instanceof Error ? error.message : error.toString(),
].join(': ');
}

export class ManifestEvaluatingError extends Error {
constructor(
message: string,
readonly details: string[],
) {
super(message);
}
}

0 comments on commit 14a3ac3

Please sign in to comment.