Skip to content

Commit d5b91a3

Browse files
authored
Ensure kv parsing returns undefined for empty cases (#115)
1 parent 9e902e2 commit d5b91a3

2 files changed

Lines changed: 100 additions & 33 deletions

File tree

src/kv.ts

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,10 @@ export function joinKVStringForGCloud(
9191
*
9292
* @param input String with key/value pairs to parse.
9393
*/
94-
export function parseKVString(input: string): KVPair {
94+
export function parseKVString(input: string): KVPair | undefined {
9595
input = (input || '').trim();
9696
if (!input) {
97-
return {};
97+
return undefined;
9898
}
9999

100100
const result: KVPair = {};
@@ -164,11 +164,11 @@ export function parseKVString(input: string): KVPair {
164164
*
165165
* @param filePath Path to the file on disk to parse.
166166
*/
167-
export function parseKVFile(filePath: string): KVPair {
167+
export function parseKVFile(filePath: string): KVPair | undefined {
168168
try {
169169
const content = presence(readFileSync(filePath, 'utf8'));
170170
if (!content || content.length < 1) {
171-
return {};
171+
return undefined;
172172
}
173173

174174
if (content[0] === '{' || content[0] === '[') {
@@ -199,9 +199,13 @@ export function parseKVFile(filePath: string): KVPair {
199199
*
200200
* @return List of key=value pairs.
201201
*/
202-
export function parseKVJSON(str: string): KVPair {
202+
export function parseKVJSON(str: string): KVPair | undefined {
203203
str = (str || '').trim();
204204
if (!str) {
205+
return undefined;
206+
}
207+
208+
if (str === '{}') {
205209
return {};
206210
}
207211

@@ -243,11 +247,18 @@ export function parseKVJSON(str: string): KVPair {
243247
*
244248
* @param str YAML content to parse as K=V pairs.
245249
*/
246-
export function parseKVYAML(str: string): KVPair {
247-
if (!str || str.trim().length === 0) {
250+
export function parseKVYAML(str: string): KVPair | undefined {
251+
const trimmed = (str || '').trim();
252+
if (!trimmed) {
253+
return undefined;
254+
}
255+
256+
if (trimmed === '{}') {
248257
return {};
249258
}
250259

260+
// Parse the original string here, since trimming could have changed
261+
// indentation.
251262
const yamlContent = YAML.parse(str) as KVPair;
252263

253264
const result: KVPair = {};
@@ -270,21 +281,15 @@ export function parseKVYAML(str: string): KVPair {
270281
* @param kvString String of KEY=VALUE pairs.
271282
* @param kvFilePath Path on disk to a YAML file of KEY: VALUE pairs.
272283
*/
273-
export function parseKVStringAndFile(kvString?: string, kvFilePath?: string): KVPair {
284+
export function parseKVStringAndFile(kvString?: string, kvFilePath?: string): KVPair | undefined {
274285
kvString = (kvString || '').trim();
275286
kvFilePath = (kvFilePath || '').trim();
276287

277-
let result: Record<string, string> = {};
288+
const fromFile = kvFilePath ? parseKVFile(kvFilePath) : undefined;
289+
const fromString = kvString ? parseKVString(kvString) : undefined;
278290

279-
if (kvFilePath) {
280-
const parsed = parseKVFile(kvFilePath);
281-
result = { ...result, ...parsed };
291+
if (fromFile === undefined && fromString === undefined) {
292+
return undefined;
282293
}
283-
284-
if (kvString) {
285-
const parsed = parseKVString(kvString);
286-
result = { ...result, ...parsed };
287-
}
288-
289-
return result;
294+
return Object.assign({}, fromFile, fromString);
290295
}

tests/kv.test.ts

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ describe('kv', { concurrency: true }, async () => {
106106
{
107107
name: 'empty string',
108108
input: '',
109+
expected: undefined,
110+
},
111+
{
112+
name: 'braces',
113+
input: '{}',
109114
expected: {},
110115
},
111116
{
@@ -257,6 +262,11 @@ ftyRyC/83GkAjs88l5eGxNE=
257262
{
258263
name: 'empty string',
259264
input: '',
265+
expected: undefined,
266+
},
267+
{
268+
name: 'braces',
269+
input: '{}',
260270
expected: {},
261271
},
262272
{
@@ -300,6 +310,11 @@ ftyRyC/83GkAjs88l5eGxNE=
300310
{
301311
name: 'empty string',
302312
input: '',
313+
expected: undefined,
314+
},
315+
{
316+
name: 'braces',
317+
input: '{}',
303318
expected: {},
304319
},
305320
{
@@ -382,21 +397,68 @@ ftyRyC/83GkAjs88l5eGxNE=
382397
});
383398

384399
test('#parseKVStringAndFile', async (suite) => {
385-
await suite.test('handles null kvString and kvFilePath', async () => {
386-
const kvString = '';
387-
const kvFile = '';
388-
389-
const result = parseKVStringAndFile(kvString, kvFile);
390-
assert.deepStrictEqual(result, {});
391-
});
400+
const cases = [
401+
{
402+
name: 'undefined kvstring and kvfile',
403+
kvString: undefined,
404+
kvFileContents: undefined,
405+
expected: undefined,
406+
},
407+
{
408+
name: 'empty kvstring and kvfile',
409+
kvString: '',
410+
kvFileContents: '',
411+
expected: undefined,
412+
},
413+
{
414+
name: 'braces kvstring and undefined kvfile',
415+
kvString: '{}',
416+
kvFileContents: undefined,
417+
expected: {},
418+
},
419+
{
420+
name: 'undefined kvstring and braces kvfile',
421+
kvString: undefined,
422+
kvFileContents: '{}',
423+
expected: {},
424+
},
425+
{
426+
name: 'braces kvstring and braces kvfile',
427+
kvString: '{}',
428+
kvFileContents: '{}',
429+
expected: {},
430+
},
431+
{
432+
name: 'partial kvstring and braces kvfile',
433+
kvString: 'FOO=bar',
434+
kvFileContents: '{}',
435+
expected: { FOO: 'bar' },
436+
},
437+
{
438+
name: 'braces kvstring and partial kvfile',
439+
kvString: '{}',
440+
kvFileContents: 'FOO=bar',
441+
expected: { FOO: 'bar' },
442+
},
443+
{
444+
name: 'merges with kvstring taking precedence',
445+
kvString: 'FOO=bar',
446+
kvFileContents: 'FOO=zap,ZIP=zap',
447+
expected: { FOO: 'bar', ZIP: 'zap' },
448+
},
449+
];
392450

393-
await suite.test('merges kvString and kvFile', async () => {
394-
const kvString = `FOO=other foo`;
395-
const kvFile = randomFilepath();
396-
await fs.writeFile(kvFile, `FOO: 'bar'\nZIP: 'zap'`);
451+
for await (const tc of cases) {
452+
await suite.test(tc.name, async () => {
453+
let kvFile = '';
454+
if (tc.kvFileContents !== undefined) {
455+
kvFile = randomFilepath();
456+
await fs.writeFile(kvFile, tc.kvFileContents);
457+
}
397458

398-
const result = parseKVStringAndFile(kvString, kvFile);
399-
assert.deepStrictEqual(result, { FOO: 'other foo', ZIP: 'zap' });
400-
});
459+
const result = parseKVStringAndFile(tc.kvString, kvFile);
460+
assert.deepStrictEqual(result, tc.expected);
461+
});
462+
}
401463
});
402464
});

0 commit comments

Comments
 (0)