Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 64 additions & 41 deletions packages/mdctl-cli/lib/env/export.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const fs = require('fs'),
{
searchParamsToObject
} = require('@medable/mdctl-core-utils'),
os = require('os'),
{ Config, Fault } = require('@medable/mdctl-core'),
ExportStream = require('@medable/mdctl-core/streams/export_stream'),
ExportFileTreeAdapter = require('@medable/mdctl-export-adapter-tree'),
Expand All @@ -24,9 +25,12 @@ const fs = require('fs'),
exportEnv = async(input) => {

const options = isSet(input) ? input : {},
{ manifest: optionsManifest } = options,
client = options.client || new Client({ ...Config.global.client, ...options }),
outputDir = options.dir || process.cwd(),
{ manifest: optionsManifest } = options

let manifest = {}

const client = options.client || new Client({ ...Config.global.client, ...options }),
outputDir = options.dir || process.cwd(),
packageFile = options.package || `${outputDir}/package.${options.format || 'json'}`,
// stream = ndjson.parse(),
url = new URL('/developer/environment/export', client.environment.url),
Expand All @@ -44,10 +48,11 @@ const fs = require('fs'),
clearOutput: options.clear
},
streamTransform = new ExportStream(),
adapter = options.adapter || new ExportFileTreeAdapter(outputDir, streamOptions),
adapter = options.adapter || new ExportFileTreeAdapter(outputDir, streamOptions)
// eslint-disable-next-line max-len
lockUnlock = new LockUnlock(outputDir, client.environment.endpoint, client.environment.env),
memo = {},
memo = {}

logStream = new Transform({
objectMode: true,
transform(chunk, encoding, cb) {
Expand All @@ -58,7 +63,7 @@ const fs = require('fs'),
})

let manifestFile,
inputStream,
inputStream
preExport = () => {},
postExport = () => {}

Expand All @@ -72,8 +77,7 @@ const fs = require('fs'),
if (!options.stream) {

let pkg,
script,
manifest = {}
script

const getScript = (...params) => {
for (const param of params) { // eslint-disable-line no-restricted-syntax
Expand Down Expand Up @@ -132,15 +136,28 @@ const fs = require('fs'),
}
}

if (optionsManifest) {
manifest = optionsManifest
} else if (pkg && pkg.manifest) {
if (optionsManifest) {
const expandedManifest = optionsManifest.startsWith('~')
? path.join(os.homedir(), optionsManifest.slice(1))
: optionsManifest

manifestFile = path.isAbsolute(expandedManifest)
? expandedManifest
: path.join(outputDir, expandedManifest)

if (!fs.existsSync(manifestFile)) {
throw Fault.create('kNotFound', { reason: `Manifest file not found: ${manifestFile}` })
}

manifest = JSON.parse(fs.readFileSync(manifestFile, 'utf8'))
} else if (pkg && pkg.manifest) {
manifestFile = `${outputDir}/${pkg.manifest}`
} else if (fs.existsSync(`${outputDir}/manifest.${options.format || 'json'}`)) {
manifestFile = `${outputDir}/manifest.${options.format || 'json'}`
}
}
// console.log('Using manifest:', JSON.stringify(manifest, null, 2))

if (fs.existsSync(manifestFile)) {
if (manifestFile && !optionsManifest && fs.existsSync(manifestFile)) {
try {
manifest = parseString(fs.readFileSync(manifestFile), options.format)
} catch (e) {
Expand All @@ -162,38 +179,44 @@ const fs = require('fs'),
}

return new Promise((resolve, reject) => {
const resultStream = pump(inputStream, streamTransform, logStream, adapter, async(err) => {

try {
await postExport({
client, err, options, memo
})
} catch (e) {
return reject(e)
}
const isConsoleAdapter =
adapter && adapter.constructor && adapter.constructor.name === 'ExportConsoleAdapter'

if (err) {
return reject(err)
}
let resultStream

if (!streamTransform.complete()) {
return reject(new Error('Export not complete!'))
}
if (isConsoleAdapter) {
resultStream = pump(inputStream, logStream, adapter, async (err) => {
if (err) return reject(err)

if (options.docs) {
console.log('Documenting env')
return Docs.generateDocumentation({
destination: path.join(outputDir, 'docs'),
source: path.join(outputDir),
module: 'env',
}).then(() => {
resolve(resultStream)
})
}
return resolve(resultStream)
try {
await postExport({ client, err, options, memo })
} catch (e) {
return reject(e)
}

})
})
return resolve(resultStream)
})
} else {
resultStream = pump(inputStream, streamTransform, logStream, adapter, async (err) => {
if (err) return reject(err)

try {
await postExport({ client, err, options, memo })
} catch (e) {
return reject(e)
}

module.exports = exportEnv
if (!streamTransform.complete()) {
return reject(new Error('Export not complete!'))
}

return resolve(resultStream)
})
}

})

}

module.exports = exportEnv
108 changes: 108 additions & 0 deletions packages/mdctl-cli/test/lib/env/export.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@

/* eslint-disable import/no-extraneous-dependencies */
const { assert } = require('chai'),
fs = require('fs'),
path = require('path'),
glob = require('glob'),
rimraf = require('rimraf'),
ndjson = require('ndjson'),
ExportConsoleAdapter = require('@medable/mdctl-export-adapter-console'),
{ Client } = require('@medable/mdctl-api'),
exportEnv = require('../../../lib/env/export')

// New Helper added here
const makeClient = () => ({
environment: {
url: 'https://localhost',
endpoint: 'localhost',
env: 'test'
}
})

describe('Environment Export', () => {

let blob,
Expand Down Expand Up @@ -110,6 +121,8 @@ describe('Environment Export', () => {
}),
adapter = new ExportConsoleAdapter({ print: false })

fs.mkdirSync(tempDir, { recursive: true })

return exportEnv({
client,
adapter,
Expand All @@ -126,3 +139,98 @@ describe('Environment Export', () => {
})

})

// New Test Section added here
describe('Environment Export -- manifest option', () => {

let tempDir, manifestContent, capturedBody

beforeEach(() => {
tempDir = path.join(process.cwd(), `output-manifest-test-${new Date().getTime()}`)
manifestContent = { scripts: { includes: ['*'] } }
capturedBody = null
fs.mkdirSync(tempDir, { recursive: true })
})

afterEach(() => {
rimraf.sync(tempDir)
})

const makeCallMock = () => async(pathname, opts) => {
capturedBody = opts.body
opts.stream.write(JSON.stringify({ object: 'manifest-exports' }) + '\n')
opts.stream.end()
}

it('resolves an absolute --manifest path and sends parsed object to API', async() => {
const manifestFile = path.join(tempDir, 'my-manifest.json')
fs.writeFileSync(manifestFile, JSON.stringify(manifestContent))

const client = { ...makeClient(), call: makeCallMock() }

await exportEnv({ client, dir: tempDir, manifest: manifestFile, format: 'json' })

assert.deepEqual(capturedBody.manifest, manifestContent,
'--manifest absolute path should be parsed and sent as object, not as a string')
})

it('expands ~ in --manifest path to the home directory', async() => {
const os = require('os')
const manifestFile = path.join(tempDir, 'tilde-manifest.json')
fs.writeFileSync(manifestFile, JSON.stringify(manifestContent))

const relativeToCwd = path.relative(os.homedir(), manifestFile)
const tildeManifest = `~/${relativeToCwd}`

const client = { ...makeClient(), call: makeCallMock() }

await exportEnv({ client, dir: tempDir, manifest: tildeManifest, format: 'json' })

assert.deepEqual(capturedBody.manifest, manifestContent,
'--manifest with ~ should be expanded to the home directory')
})

it('resolves a relative --manifest path against the output dir', async() => {
const manifestFile = path.join(tempDir, 'custom-manifest.json')
fs.writeFileSync(manifestFile, JSON.stringify(manifestContent))

const client = { ...makeClient(), call: makeCallMock() }

await exportEnv({ client, dir: tempDir, manifest: 'custom-manifest.json', format: 'json' })

assert.deepEqual(capturedBody.manifest, manifestContent,
'--manifest relative path should be resolved against outputDir and parsed')
})

it('sends ec__ objects manifest with env includes and 7 named objects', async() => {
const sampleManifest = {
env: { includes: ['*'] },
object: 'manifest',
objects: [
{ includes: ['*'], name: 'ec__default_document_css' },
{ includes: ['*'], name: 'ec__document_datum' },
{ includes: ['*'], name: 'ec__document_invite' },
{ includes: ['*'], name: 'ec__document_template' },
{ includes: ['*'], name: 'ec__knowledge_check' },
{ includes: ['*'], name: 'ec__linked_field' },
{ includes: ['*'], name: 'ec__signed_document' }
]
}
const manifestFile = path.join(tempDir, 'manifest.json')
fs.writeFileSync(manifestFile, JSON.stringify(sampleManifest))

const client = { ...makeClient(), call: makeCallMock() }

await exportEnv({ client, dir: tempDir, format: 'json' })

assert.deepEqual(capturedBody.manifest, sampleManifest,
'manifest with ec__ objects should be auto-discovered and sent as parsed object')
assert.strictEqual(capturedBody.manifest.object, 'manifest')
assert.strictEqual(capturedBody.manifest.objects.length, 7)
assert.isTrue(
capturedBody.manifest.objects.every(o => o.includes[0] === '*'),
'all objects should have wildcard includes'
)
})

})
Loading