Skip to content

Commit

Permalink
feat(template): add template helper functions
Browse files Browse the repository at this point in the history
This adds a collection of helper functions to our template syntax.

Initially we've added a handful of specific helpers that users have
requested, but we'll be very happy to add more since that generally is
very simple to do now that the basic structure and syntax support is in
place.

See the added docs for the usage instructions and the helpers available
in the first iteration.

_Reviewers, please don't hesitate to suggest more functions to include
in the first iteration!_
  • Loading branch information
edvald authored and thsig committed Mar 30, 2021
1 parent 610c93f commit c08afe7
Show file tree
Hide file tree
Showing 35 changed files with 630 additions and 79 deletions.
4 changes: 2 additions & 2 deletions core/gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ const { resolve, join } = require("path")
const gulp = require("gulp")
const pegjs = require("gulp-pegjs")

const pegjsSources = resolve(__dirname, "src", "*.pegjs")
const pegjsSources = resolve(__dirname, "src", "template-string", "*.pegjs")
const destDir = resolve(__dirname, "build")

gulp.task("pegjs", () =>
gulp.src(pegjsSources)
.pipe(pegjs({ format: "commonjs" }))
.pipe(gulp.dest(join(destDir, "src"))),
.pipe(gulp.dest(join(destDir, "src", "template-string"))),
)

gulp.task("pegjs-watch", () =>
Expand Down
2 changes: 1 addition & 1 deletion core/src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ import { StopPortForwardParams } from "./types/plugin/service/stopPortForward"
import { emptyRuntimeContext, RuntimeContext } from "./runtime-context"
import { GetServiceStatusTask } from "./tasks/get-service-status"
import { getServiceStatuses } from "./tasks/base"
import { getRuntimeTemplateReferences, resolveTemplateStrings } from "./template-string"
import { getRuntimeTemplateReferences, resolveTemplateStrings } from "./template-string/template-string"
import { getPluginBases, getPluginDependencies, getModuleTypeBases } from "./plugins"
import { ConfigureProviderParams, ConfigureProviderResult } from "./types/plugin/provider/configureProvider"
import { GardenTask } from "./types/task"
Expand Down
2 changes: 1 addition & 1 deletion core/src/commands/run/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
WorkflowStepConfigContext,
WorkflowStepResult,
} from "../../config/template-contexts/workflow"
import { resolveTemplateStrings, resolveTemplateString } from "../../template-string"
import { resolveTemplateStrings, resolveTemplateString } from "../../template-string/template-string"
import { ConfigurationError, FilesystemError } from "../../exceptions"
import { posix, join } from "path"
import { ensureDir, writeFile } from "fs-extra"
Expand Down
13 changes: 13 additions & 0 deletions core/src/config/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,19 @@ interface MetadataKeys {
keyPlaceholder?: string
}

// Need this to fix the Joi typing
export interface JoiDescription extends Joi.Description {
type: string
name: string
level: number
flags?: {
default?: any
description?: string
presence?: string
only?: boolean
}
}

// Unfortunately we need to explicitly extend each type (just extending the AnySchema doesn't work).
declare module "@hapi/joi" {
export interface AnySchema {
Expand Down
2 changes: 1 addition & 1 deletion core/src/config/module-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { baseModuleSpecSchema, BaseModuleSpec, ModuleConfig } from "./module"
import { dedent, deline } from "../util/string"
import { GardenResource, prepareModuleResource } from "./base"
import { DOCS_BASE_URL } from "../constants"
import { resolveTemplateStrings } from "../template-string"
import { resolveTemplateStrings } from "../template-string/template-string"
import { validateWithPath } from "./validation"
import { Garden } from "../garden"
import { ConfigurationError } from "../exceptions"
Expand Down
2 changes: 1 addition & 1 deletion core/src/config/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
joiSparseArray,
} from "./common"
import { validateWithPath } from "./validation"
import { resolveTemplateStrings } from "../template-string"
import { resolveTemplateStrings } from "../template-string/template-string"
import { ProjectConfigContext, EnvironmentConfigContext } from "./template-contexts/project"
import { findByName, getNames } from "../util/util"
import { ConfigurationError, ParameterError, ValidationError } from "../exceptions"
Expand Down
2 changes: 1 addition & 1 deletion core/src/config/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import { deline } from "../util/string"
import { joiIdentifier, joiUserIdentifier, joiArray, joi, joiIdentifierMap, joiSparseArray } from "./common"
import { collectTemplateReferences } from "../template-string"
import { collectTemplateReferences } from "../template-string/template-string"
import { ConfigurationError } from "../exceptions"
import { ModuleConfig, moduleConfigSchema } from "./module"
import { uniq } from "lodash"
Expand Down
2 changes: 1 addition & 1 deletion core/src/config/template-contexts/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
resolveTemplateString,
TemplateStringMissingKeyException,
TemplateStringPassthroughException,
} from "../../template-string"
} from "../../template-string/template-string"
import { joi } from "../common"
import { KeyedSet } from "../../util/keyed-set"
import { naturalList } from "../../util/string"
Expand Down
8 changes: 5 additions & 3 deletions core/src/config/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,9 @@ export function validateSchema<T>(

const errorDetails = error.details.map((e) => {
// render the key path in a much nicer way
let renderedPath = "."
let renderedPath = ""

if (e.path.length) {
renderedPath = ""
let d = description

for (const part of e.path) {
Expand All @@ -118,7 +117,10 @@ export function validateSchema<T>(
}

// a little hack to always use full key paths instead of just the label
e.message = e.message.replace(joiLabelPlaceholderRegex, "key " + chalk.underline(renderedPath || "."))
e.message = e.message.replace(
joiLabelPlaceholderRegex,
renderedPath ? "key " + chalk.underline(renderedPath) : "value"
)
e.message = e.message.replace(joiPathPlaceholderRegex, chalk.underline(renderedPath || "."))
// FIXME: remove once we've customized the error output from AJV in customObject.jsonSchema()
e.message = e.message.replace(/should NOT have/g, "should not have")
Expand Down
2 changes: 1 addition & 1 deletion core/src/config/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { deline, dedent } from "../util/string"
import { defaultContainerLimits, ServiceLimitSpec } from "../plugins/container/config"
import { Garden } from "../garden"
import { WorkflowConfigContext } from "./template-contexts/workflow"
import { resolveTemplateStrings } from "../template-string"
import { resolveTemplateStrings } from "../template-string/template-string"
import { validateWithPath } from "./validation"
import { ConfigurationError } from "../exceptions"
import { getCoreCommands } from "../commands/commands"
Expand Down
4 changes: 2 additions & 2 deletions core/src/docs/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { resolve } from "path"
import { projectDocsSchema } from "../config/project"
import { get, isFunction } from "lodash"
import handlebars = require("handlebars")
import { joi } from "../config/common"
import { joi, JoiDescription } from "../config/common"
import { STATIC_DIR } from "../constants"
import {
indent,
Expand All @@ -24,7 +24,7 @@ import {
flattenSchema,
isArrayType,
} from "./common"
import { JoiDescription, JoiKeyDescription } from "./joi-schema"
import { JoiKeyDescription } from "./joi-schema"
import { safeDumpYaml } from "../util/util"

export const TEMPLATES_DIR = resolve(STATIC_DIR, "docs", "templates")
Expand Down
15 changes: 1 addition & 14 deletions core/src/docs/joi-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import Joi from "@hapi/joi"
import { uniq, isFunction, extend, isArray, isPlainObject } from "lodash"
import { BaseKeyDescription, isArrayType } from "./common"
import { findByName, safeDumpYaml } from "../util/util"
import { JsonKeyDescription } from "./json-schema"

// Need this to fix the Joi typing
export interface JoiDescription extends Joi.Description {
type: string
name: string
level: number
flags?: {
default?: any
description?: string
presence?: string
only?: boolean
}
}
import { JoiDescription } from "../config/common"

export class JoiKeyDescription extends BaseKeyDescription {
private joiDescription: JoiDescription
Expand Down
3 changes: 3 additions & 0 deletions core/src/docs/template-strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { ProjectConfigContext, EnvironmentConfigContext } from "../config/templa
import { ProviderConfigContext } from "../config/template-contexts/provider"
import { ModuleConfigContext, OutputConfigContext } from "../config/template-contexts/module"
import { WorkflowStepConfigContext } from "../config/template-contexts/workflow"
import { helperFunctions } from "../template-string/functions"
import { sortBy } from "lodash"

export function writeTemplateStringReferenceDocs(docsRoot: string) {
const referenceDir = resolve(docsRoot, "reference")
Expand Down Expand Up @@ -47,6 +49,7 @@ export function writeTemplateStringReferenceDocs(docsRoot: string) {
const templatePath = resolve(TEMPLATES_DIR, "template-strings.hbs")
const template = handlebars.compile(readFileSync(templatePath).toString())
const markdown = template({
helperFunctions: sortBy(Object.values(helperFunctions), "name"),
projectContext,
environmentContext,
providerContext,
Expand Down
4 changes: 4 additions & 0 deletions core/src/exceptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,7 @@ export class WorkflowScriptError extends GardenBaseError {
export class EnterpriseApiError extends GardenBaseError {
type = "enterprise-api"
}

export class TemplateStringError extends GardenBaseError {
type = "template-string"
}
6 changes: 5 additions & 1 deletion core/src/garden.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,11 @@ import { ensureConnected } from "./db/connection"
import { DependencyValidationGraph } from "./util/validate-dependencies"
import { Profile } from "./util/profiling"
import username from "username"
import { throwOnMissingSecretKeys, resolveTemplateString, resolveTemplateStrings } from "./template-string"
import {
throwOnMissingSecretKeys,
resolveTemplateString,
resolveTemplateStrings,
} from "./template-string/template-string"
import { WorkflowConfig, WorkflowConfigMap, resolveWorkflowConfig } from "./config/workflow"
import { PluginTool, PluginTools } from "./util/ext-tools"
import {
Expand Down
2 changes: 1 addition & 1 deletion core/src/outputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import { Garden } from "./garden"
import { collectTemplateReferences, resolveTemplateStrings } from "./template-string"
import { collectTemplateReferences, resolveTemplateStrings } from "./template-string/template-string"
import { OutputConfigContext } from "./config/template-contexts/module"
import { emptyRuntimeContext, prepareRuntimeContext } from "./runtime-context"
import { DeployTask } from "./tasks/deploy"
Expand Down
6 changes: 5 additions & 1 deletion core/src/resolve-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@

import { cloneDeep, keyBy } from "lodash"
import { validateWithPath } from "./config/validation"
import { resolveTemplateStrings, getModuleTemplateReferences, resolveTemplateString } from "./template-string"
import {
resolveTemplateStrings,
getModuleTemplateReferences,
resolveTemplateString,
} from "./template-string/template-string"
import { ContextResolveOpts, GenericContext } from "./config/template-contexts/base"
import { relative, resolve, posix, dirname } from "path"
import { Garden } from "./garden"
Expand Down
2 changes: 1 addition & 1 deletion core/src/tasks/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { Garden } from "../garden"
import { LogEntry } from "../logger/log-entry"
import { ConfigGraph } from "../config-graph"
import { emptyRuntimeContext } from "../runtime-context"
import { resolveTemplateString } from "../template-string"
import { resolveTemplateString } from "../template-string/template-string"
import { joi } from "../config/common"
import { versionStringPrefix } from "../vcs/vcs"
import { ConfigContext, schema } from "../config/template-contexts/base"
Expand Down
2 changes: 1 addition & 1 deletion core/src/tasks/resolve-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
getProviderTemplateReferences,
ProviderMap,
} from "../config/provider"
import { resolveTemplateStrings } from "../template-string"
import { resolveTemplateStrings } from "../template-string/template-string"
import { ConfigurationError, PluginError } from "../exceptions"
import { keyBy, omit, flatten, uniq } from "lodash"
import { GraphResults } from "../task-graph"
Expand Down

0 comments on commit c08afe7

Please sign in to comment.