Skip to content

Commit

Permalink
fix(create-command): add project key to generated config and fix tests
Browse files Browse the repository at this point in the history
The "project" key was missing at the top of the project level garden.yml
file generated by the create command. Furthermore, the tests were
validating the generated config against the projectSchema instead of the
entire configSchema.
  • Loading branch information
eysi09 committed Nov 19, 2018
1 parent 7322703 commit 63cca8f
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 91 deletions.
62 changes: 31 additions & 31 deletions garden-service/src/commands/create/config-templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@
*/

import { capitalize, camelCase, uniq } from "lodash"
import * as Joi from "joi"

import { DeepPartial } from "../../util/util"
import { ContainerModuleSpec } from "../../plugins/container"
import { GcfModuleSpec } from "../../plugins/google/google-cloud-functions"
import { ProjectConfig } from "../../config/project"
import { BaseModuleSpec, ModuleConfig, baseModuleSpecSchema } from "../../config/module"
import { ModuleConfig } from "../../config/module"

/**
* Ideally there would be some mechanism to discover available module types,
Expand All @@ -32,23 +31,12 @@ export const availableModuleTypes = <ModuleType[]>Object.keys(MODULE_PROVIDER_MA

export type ModuleType = keyof typeof MODULE_PROVIDER_MAP

export const moduleSchema = Joi.object().keys({
module: baseModuleSpecSchema,
})

export interface ConfigOpts {
name: string
path: string
config: { module: Partial<ModuleConfig> } | Partial<ProjectConfig>
}

export interface ModuleConfigOpts extends ConfigOpts {
type: ModuleType
config: { module: Partial<ModuleConfig> }
export interface ProjectTemplate {
project: Partial<ProjectConfig>
}

export interface ProjectConfigOpts extends ConfigOpts {
config: Partial<ProjectConfig>
export interface ModuleTemplate {
module: Partial<ModuleConfig>
}

const noCase = (str: string) => str.replace(/-|_/g, " ")
Expand Down Expand Up @@ -85,22 +73,34 @@ export function npmPackageTemplate(_moduleName: string): any {
return {}
}

export const projectTemplate = (name: string, moduleTypes: ModuleType[]): Partial<ProjectConfig> => {
export const projectTemplate = (name: string, moduleTypes: ModuleType[]): ProjectTemplate => {
const providers = uniq(moduleTypes).map(type => ({ name: MODULE_PROVIDER_MAP[type] }))
return {
name,
environments: [
{
name: "local",
providers,
variables: {},
},
],
project: {
name,
environments: [
{
name: "local",
providers,
variables: {},
},
],
},
}
}

export const moduleTemplate = (name: string, type: ModuleType): Partial<BaseModuleSpec> => ({
name,
type,
description: `${titleize(name)} ${noCase(type)}`,
})
export const moduleTemplate = (name: string, type: ModuleType): ModuleTemplate => {
const moduleTypeTemplate = {
container: containerTemplate,
"google-cloud-function": googleCloudFunctionTemplate,
"npm-package": npmPackageTemplate,
}[type]
return {
module: {
name,
type,
description: `${titleize(name)} ${noCase(type)}`,
...moduleTypeTemplate(name),
},
}
}
24 changes: 5 additions & 19 deletions garden-service/src/commands/create/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,28 @@

import * as Joi from "joi"
import {
containerTemplate,
googleCloudFunctionTemplate,
npmPackageTemplate,
ModuleConfigOpts,
ModuleType,
moduleTemplate,
ConfigOpts,
} from "./config-templates"
import { join } from "path"
import { pathExists } from "fs-extra"
import { validate } from "../../config/common"
import { dumpYaml } from "../../util/util"
import { MODULE_CONFIG_FILENAME } from "../../constants"
import { LogNode } from "../../logger/log-node"
import { NewModuleOpts, CommonOpts } from "./project"

export function prepareNewModuleConfig(name: string, type: ModuleType, path: string): ModuleConfigOpts {
const moduleTypeTemplate = {
container: containerTemplate,
"google-cloud-function": googleCloudFunctionTemplate,
"npm-package": npmPackageTemplate,
}[type]
export function prepareNewModuleOpts(name: string, type: ModuleType, path: string): NewModuleOpts {
return {
name,
type,
path,
config: {
module: {
...moduleTemplate(name, type),
...moduleTypeTemplate(name),
},
},
config: moduleTemplate(name, type),
}
}

export async function dumpConfig(configOpts: ConfigOpts, schema: Joi.Schema, logger: LogNode) {
const { config, name, path } = configOpts
export async function dumpConfig(opts: CommonOpts, schema: Joi.Schema, logger: LogNode) {
const { config, name, path } = opts
const yamlPath = join(path, MODULE_CONFIG_FILENAME)
const task = logger.info({
msg: `Writing config for ${name}`,
Expand Down
16 changes: 9 additions & 7 deletions garden-service/src/commands/create/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import { basename, join } from "path"
import dedent = require("dedent")
import { ensureDir } from "fs-extra"

import {
Command,
Expand All @@ -17,14 +18,15 @@ import {
CommandParams,
} from "../base"
import { ParameterError, GardenBaseError } from "../../exceptions"
import { availableModuleTypes, ModuleType, moduleSchema, ModuleConfigOpts } from "./config-templates"
import { availableModuleTypes, ModuleType } from "./config-templates"
import {
prepareNewModuleConfig,
prepareNewModuleOpts,
dumpConfig,
} from "./helpers"
import { prompts } from "./prompts"
import { validate, joiIdentifier } from "../../config/common"
import { ensureDir } from "fs-extra"
import { NewModuleOpts } from "./project"
import { configSchema } from "../../config/base"

const createModuleOptions = {
name: new StringParameter({
Expand All @@ -47,7 +49,7 @@ type Opts = typeof createModuleOptions

interface CreateModuleResult extends CommandResult {
result: {
module?: ModuleConfigOpts,
module?: NewModuleOpts,
}
}

Expand Down Expand Up @@ -102,14 +104,14 @@ export class CreateModuleCommand extends Command<Args, Opts> {
}
}

const module = prepareNewModuleConfig(moduleName, type, moduleRoot)
const moduleOpts = prepareNewModuleOpts(moduleName, type, moduleRoot)
try {
await dumpConfig(module, moduleSchema, garden.log)
await dumpConfig(moduleOpts, configSchema, garden.log)
} catch (err) {
errors.push(err)
}
return {
result: { module },
result: { module: moduleOpts },
errors,
}
}
Expand Down
57 changes: 36 additions & 21 deletions garden-service/src/commands/create/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,21 @@ import {
} from "../base"
import { GardenBaseError } from "../../exceptions"
import {
prepareNewModuleConfig,
prepareNewModuleOpts,
dumpConfig,
} from "./helpers"
import { prompts } from "./prompts"
import {
projectTemplate,
ModuleConfigOpts,
ProjectConfigOpts,
moduleSchema,
ModuleTemplate,
ModuleType,
ProjectTemplate,
} from "./config-templates"
import { getChildDirNames } from "../../util/util"
import { validate, joiIdentifier } from "../../config/common"
import { projectSchema } from "../../config/project"
import { configSchema } from "../../config/base"

const flatten = (acc, val) => acc.concat(val)

const createProjectOptions = {
"module-dirs": new PathsParameter({
Expand All @@ -53,12 +55,25 @@ const createProjectArguments = {
type Args = typeof createProjectArguments
type Opts = typeof createProjectOptions

const flatten = (acc, val) => acc.concat(val)
export interface CommonOpts {
name: string
path: string
config: ModuleTemplate | ProjectTemplate
}

export interface NewModuleOpts extends CommonOpts {
type: ModuleType
config: ModuleTemplate
}

export interface NewProjectOpts extends CommonOpts {
config: ProjectTemplate
}

interface CreateProjectResult extends CommandResult {
result: {
projectConfig: ProjectConfigOpts,
moduleConfigs: ModuleConfigOpts[],
project: NewProjectOpts,
modules: NewModuleOpts[],
}
}

Expand Down Expand Up @@ -86,7 +101,7 @@ export class CreateProjectCommand extends Command<Args, Opts> {
options = createProjectOptions

async action({ garden, args, opts }: CommandParams<Args, Opts>): Promise<CreateProjectResult> {
let moduleConfigs: ModuleConfigOpts[] = []
let moduleOpts: NewModuleOpts[] = []
let errors: GardenBaseError[] = []

const projectRoot = args["project-dir"] ? join(garden.projectRoot, args["project-dir"].trim()) : garden.projectRoot
Expand All @@ -107,13 +122,13 @@ export class CreateProjectCommand extends Command<Args, Opts> {

if (moduleParentDirs.length > 0) {
// If module-dirs option provided we scan for modules in the parent dir(s) and add them one by one
moduleConfigs = (await Bluebird.mapSeries(moduleParentDirs, async parentDir => {
moduleOpts = (await Bluebird.mapSeries(moduleParentDirs, async parentDir => {
const moduleNames = await getChildDirNames(parentDir)

return Bluebird.reduce(moduleNames, async (acc: ModuleConfigOpts[], moduleName: string) => {
return Bluebird.reduce(moduleNames, async (acc: NewModuleOpts[], moduleName: string) => {
const { type } = await prompts.addConfigForModule(moduleName)
if (type) {
acc.push(prepareNewModuleConfig(moduleName, type, join(parentDir, moduleName)))
acc.push(prepareNewModuleOpts(moduleName, type, join(parentDir, moduleName)))
}
return acc
}, [])
Expand All @@ -122,30 +137,30 @@ export class CreateProjectCommand extends Command<Args, Opts> {
.filter(m => m)
} else {
// Otherwise we prompt the user for modules to add
moduleConfigs = (await prompts.repeatAddModule())
.map(({ name, type }) => prepareNewModuleConfig(name, type, join(projectRoot, name)))
moduleOpts = (await prompts.repeatAddModule())
.map(({ name, type }) => prepareNewModuleOpts(name, type, join(projectRoot, name)))
}

garden.log.info("---------")
const taskLog = garden.log.info({ msg: "Setting up project", status: "active" })

for (const module of moduleConfigs) {
for (const module of moduleOpts) {
await ensureDir(module.path)
try {
await dumpConfig(module, moduleSchema, garden.log)
await dumpConfig(module, configSchema, garden.log)
} catch (err) {
errors.push(err)
}
}

const projectConfig: ProjectConfigOpts = {
const projectOpts: NewProjectOpts = {
path: projectRoot,
name: projectName,
config: projectTemplate(projectName, moduleConfigs.map(module => module.type)),
config: projectTemplate(projectName, moduleOpts.map(module => module.type)),
}

try {
await dumpConfig(projectConfig, projectSchema, garden.log)
await dumpConfig(projectOpts, configSchema, garden.log)
} catch (err) {
errors.push(err)
}
Expand All @@ -161,8 +176,8 @@ export class CreateProjectCommand extends Command<Args, Opts> {

return {
result: {
moduleConfigs,
projectConfig,
modules: moduleOpts,
project: projectOpts,
},
errors,
}
Expand Down
13 changes: 6 additions & 7 deletions garden-service/test/src/commands/create/config-templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,34 @@ import {
moduleTemplate,
} from "../../../../src/commands/create/config-templates"
import { validate } from "../../../../src/config/common"
import { baseModuleSpecSchema } from "../../../../src/config/module"
import { projectSchema } from "../../../../src/config/project"
import { configSchema } from "../../../../src/config/base"

describe("ConfigTemplates", () => {
describe("projectTemplate", () => {
for (const moduleType of availableModuleTypes) {
it(`should be valid for module type ${moduleType}`, async () => {
const config = projectTemplate("my-project", [moduleType])
expect(() => validate(config, projectSchema)).to.not.throw()
expect(() => validate(config, configSchema)).to.not.throw()
})
}
it("should be valid for multiple module types", async () => {
const config = projectTemplate("my-project", availableModuleTypes)
expect(() => validate(config, projectSchema)).to.not.throw()
expect(() => validate(config, configSchema)).to.not.throw()
})
it("should be valid for multiple modules of same type", async () => {
const config = projectTemplate("my-project", [availableModuleTypes[0], availableModuleTypes[0]])
expect(() => validate(config, projectSchema)).to.not.throw()
expect(() => validate(config, configSchema)).to.not.throw()
})
it("should be valid if no modules", async () => {
const config = projectTemplate("my-project", [])
expect(() => validate(config, projectSchema)).to.not.throw()
expect(() => validate(config, configSchema)).to.not.throw()
})
})
describe("moduleTemplate", () => {
for (const moduleType of availableModuleTypes) {
it(`should be valid for module type ${moduleType}`, async () => {
const config = moduleTemplate("my-module", moduleType)
expect(() => validate(config, baseModuleSpecSchema)).to.not.throw()
expect(() => validate(config, configSchema)).to.not.throw()
})
}
})
Expand Down
Loading

0 comments on commit 63cca8f

Please sign in to comment.