Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add yaml tolm support #3

Merged
merged 2 commits into from
May 23, 2024
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
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
Loading