Skip to content

Commit

Permalink
chore: add yaml tolm support (#3)
Browse files Browse the repository at this point in the history
* chore: add YAML and TOML configuration support

* fix: tests
  • Loading branch information
danteay authored May 23, 2024
1 parent b3ebb34 commit 2abdcbd
Show file tree
Hide file tree
Showing 9 changed files with 314 additions and 10 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ process of keeping configuration files up-to-date across your S3 storage.
| `aws_region` | AWS Region | false | `us-east-1` |
| `dry_run` | Do not create any persistent changes over the configurations and just show an overview of the changes | false | false |

## Runs
## Supported formats

- **Using**: docker
- **Image**: Dockerfile
- JSON
- YAML
- TOML

## Example Usage

Expand Down
12 changes: 12 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import core from '@actions/core';
import * as inputs from './src/inputs/index.js';
import Action from './src/action.js';

/**
* Get the input parameters required for the action.
* @returns {{bucket: string, awsRegion: string, awsAccessKey: string, dryRun: boolean, destination: string, source: string, awsSecretKey: string}}
*/
const getInputs = () => {
return {
bucket: inputs.getString('bucket'),
Expand All @@ -15,6 +19,10 @@ const getInputs = () => {
};
};

/**
* Print the differences between the old and new configurations.
* @param diffs
*/
const printDifferences = (diffs) => {
core.info('Configuration differences');

Expand All @@ -31,6 +39,10 @@ const printDifferences = (diffs) => {
});
};

/**
* Main function.
* @returns {Promise<void>}
*/
const main = async () => {
const action = new Action(getInputs());

Expand Down
28 changes: 25 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
"@actions/core": "^1.10.1",
"@aws-sdk/client-s3": "^3.577.0",
"is-empty-input": "^1.1.0",
"winston": "^3.13.0"
"smol-toml": "^1.2.0",
"winston": "^3.13.0",
"yaml": "^2.4.2"
},
"devDependencies": {
"chai": "^5.1.1",
Expand Down
33 changes: 30 additions & 3 deletions src/action.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import isEmptyInput from 'is-empty-input';

import * as files from './utils/files.js';
import Parser from './parser/index.js';
import Client from './aws/s3.js';

/**
* Action class that performs the main logic of the action.
*
* @property {Object} inputs - The input parameters required for the action.
* @property {Client} s3 - An S3 client instance.
* @property {Object} oldConfig - The old configuration object.
* @property {Object} newConfig - The new configuration object.
* @property {Parser} oldConfigParser - The parser instance for the old configuration.
* @property {Parser} newConfigParser - The parser instance for the new configuration.
*/
export default class Action {
/**
* Constructor for the Action class.
Expand Down Expand Up @@ -34,6 +45,11 @@ export default class Action {

this.oldConfig = {};
this.newConfig = {};

this.oldConfigParser = null;
this.newConfigParser = null;

this.initParsers();
}

/**
Expand Down Expand Up @@ -88,7 +104,7 @@ export default class Action {
await this.s3.createFile(
this.inputs.bucket,
this.inputs.destination,
JSON.stringify(this.newConfig)
this.oldConfigParser.stringify(this.newConfig)
);
}

Expand Down Expand Up @@ -154,7 +170,7 @@ export default class Action {
this.inputs.destination
);

this.oldConfig = JSON.parse(content.toString());
this.oldConfig = this.oldConfigParser.parse(content.toString());
}

/**
Expand All @@ -177,7 +193,7 @@ export default class Action {
*/
loadNewConfig() {
const content = files.readContent(this.inputs.source);
this.newConfig = JSON.parse(content);
this.newConfig = this.newConfigParser.parse(content);
}

/**
Expand All @@ -196,4 +212,15 @@ export default class Action {
}
});
}

/**
* Initializes the oldConfigParser and newConfigParser instances based on the source and destination file formats.
*/
initParsers() {
const sourceFormat = files.extractFormat(this.inputs.source);
const destinationFormat = files.extractFormat(this.inputs.destination);

this.oldConfigParser = new Parser(sourceFormat);
this.newConfigParser = new Parser(destinationFormat);
}
}
62 changes: 62 additions & 0 deletions src/parser/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as TOML from 'smol-toml';
import * as YAML from 'yaml';

/**
* Configuration parser class that provides methods to parse and stringify configuration data.
*
* @class Parser
* @param {string} format - The format of the configuration data.
* @property {string} format - The format of the configuration data.
* @property {Object} tokenizer - The tokenizer instance for the specified format.
*/
export default class Parser {
/**
* Constructor for the Parser class.
* Initializes the class with the provided format and sets up the tokenizer instance.
*
* @param {string} format - The format of the configuration data.
*/
constructor(format) {
this.format = format;
this.tokenizer = this.getTokenizer();
}

/**
* Obtain the tokenizer instance for the specified format.
*
* @returns {{parse: Function, stringify: Function}} - The tokenizer instance for the specified format.
* @throws {Error} - Unsupported format error.
*/
getTokenizer() {
switch (this.format) {
case 'toml':
return TOML;
case 'yaml':
return YAML;
case 'json':
return JSON;
default:
throw new Error(`Unsupported format: ${this.format}`);
}
}

/**
* Parse the provided data using the tokenizer instance.
*
* @param {string} data - The configuration data to be parsed.
* @returns {Object} - The parsed configuration data.
*/
parse(data) {
return this.tokenizer.parse(data);
}

/**
* Stringify the provided data using the tokenizer instance.
*
* @param {Object} data - The configuration data to be stringified.
* @returns {string} - The stringified configuration data.
*/
stringify(data) {
return this.tokenizer.stringify(data);
}
}
26 changes: 26 additions & 0 deletions src/utils/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,29 @@ export const readContent = (filePath) => {

return content.toString();
};

/**
* Extracts the format of a file based on its extension.
*
* @param {string} fileName - The name or path of the file.
* @returns {string} - The format of the file (e.g., 'toml', 'yaml', 'json').
* @throws {Error} - Throws an error if the file extension is unsupported.
*/
export const extractFormat = (fileName) => {
// Extract the file extension
const fileExtension = fileName.split('.').pop().toLowerCase();

// Determine the format based on the file extension
switch (fileExtension) {
case 'toml':
return 'toml';
case 'yaml':
case 'yml':
return 'yaml';
case 'json':
case 'ejson':
return 'json';
default:
throw new Error(`Unsupported file format: ${fileExtension}`);
}
};
112 changes: 112 additions & 0 deletions tests/parser/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { expect } from 'chai';
import * as TOML from 'smol-toml';
import * as YAML from 'yaml';

import Parser from '../../src/parser/index.js';

describe('Parser Class', () => {
describe('Constructor', () => {
it('should initialize with format toml and set the correct tokenizer', () => {
const parser = new Parser('toml');
expect(parser.format).to.equal('toml');
expect(parser.tokenizer).to.equal(TOML);
});

it('should initialize with format yaml and set the correct tokenizer', () => {
const parser = new Parser('yaml');
expect(parser.format).to.equal('yaml');
expect(parser.tokenizer).to.equal(YAML);
});

it('should initialize with format json and set the correct tokenizer', () => {
const parser = new Parser('json');
expect(parser.format).to.equal('json');
expect(parser.tokenizer).to.equal(JSON);
});

it('should throw an error for unsupported format', () => {
expect(() => new Parser('xml')).to.throw('Unsupported format: xml');
});
});

describe('getTokenizer method', () => {
it('should return TOML tokenizer for toml format', () => {
const parser = new Parser('toml');
expect(parser.getTokenizer()).to.equal(TOML);
});

it('should return YAML tokenizer for yaml format', () => {
const parser = new Parser('yaml');
expect(parser.getTokenizer()).to.equal(YAML);
});

it('should return JSON tokenizer for json format', () => {
const parser = new Parser('json');
expect(parser.getTokenizer()).to.equal(JSON);
});

it('should throw an error for unsupported format', () => {
expect(() => {
new Parser('xml');
}).to.throw('Unsupported format: xml');
});
});

describe('parse method', () => {
it('should correctly parse TOML data', () => {
const parser = new Parser('toml');
const tomlData = 'key = "value"';
const expectedData = { key: 'value' };

const result = parser.parse(tomlData);
expect(result).to.deep.equal(expectedData);
});

it('should correctly parse YAML data', () => {
const parser = new Parser('yaml');
const yamlData = 'key: value';
const expectedData = { key: 'value' };

const result = parser.parse(yamlData);
expect(result).to.deep.equal(expectedData);
});

it('should correctly parse JSON data', () => {
const parser = new Parser('json');
const jsonData = '{"key": "value"}';
const expectedData = { key: 'value' };

const result = parser.parse(jsonData);
expect(result).to.deep.equal(expectedData);
});
});

describe('stringify method', () => {
it('should correctly stringify TOML data', () => {
const parser = new Parser('toml');
const data = { key: 'value' };
const expectedTomlData = 'key = "value"';

const result = parser.stringify(data);
expect(result).to.equal(expectedTomlData);
});

it('should correctly stringify YAML data', () => {
const parser = new Parser('yaml');
const data = { key: 'value' };
const expectedYamlData = 'key: value\n';

const result = parser.stringify(data);
expect(result).to.equal(expectedYamlData);
});

it('should correctly stringify JSON data', () => {
const parser = new Parser('json');
const data = { key: 'value' };
const expectedJsonData = '{"key":"value"}';

const result = parser.stringify(data);
expect(result).to.equal(expectedJsonData);
});
});
});
Loading

0 comments on commit 2abdcbd

Please sign in to comment.