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

Add copy option - messages are not deleted from source queue #24

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ $ aws-move-queue-messages
With all optional CLI arguments:

```sh
$ aws-move-queue-messages <from-queue-url> <to-queue-url> -r [AWS-REGION] -y
$ aws-move-queue-messages <from-queue-url> <to-queue-url> -r [AWS-REGION] -m 100 -y
```

Any CLI argument or option you do not specify will fallback to a CLI prompt. The `-y` CLI option will answer the confirmation prompt automatically with "yes".
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"license": "MIT",
"scripts": {
"lint": "eslint . --cache",
"lint:fix": "eslint . --fix --cache",
"test": "jest",
"test:watch": "yarn test --watch --onlyChanged",
"test:coverage": "yarn test --coverage",
Expand Down
10 changes: 10 additions & 0 deletions src/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ const validateUrl = (input) => {
return valid;
};

const validateMaxMessages = (input) => {
let valid = true;
if (!input.match(/^[1-9][0-9]*$/)) {
valid = 'Please enter a valid max number of messages greater than 0!';
}

return valid;
};

module.exports = {
validateUrl,
validateMaxMessages,
};
29 changes: 28 additions & 1 deletion src/helper.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @jest-environment node
*/

const { validateUrl } = require('./helper');
const { validateUrl, validateMaxMessages } = require('./helper');

describe('validateUrl', () => {
test('to pass invalid url', () => {
Expand All @@ -20,3 +20,30 @@ describe('validateUrl', () => {
expect(validate).toBe(true);
});
});

describe('validateMaxMessages', () => {
test('to pass an invalid value with numeric and alpha chars', () => {
const validate = validateMaxMessages('1abc');
expect(validate).toEqual('Please enter a valid max number of messages greater than 0!');
});

test('to pass an invalid value with only alpha chars', () => {
const validate = validateMaxMessages('abc');
expect(validate).toEqual('Please enter a valid max number of messages greater than 0!');
});

test('to pass an empty value', () => {
const validate = validateMaxMessages('');
expect(validate).toEqual('Please enter a valid max number of messages greater than 0!');
});

test('to pass negative number', () => {
const validate = validateMaxMessages('-1');
expect(validate).toEqual('Please enter a valid max number of messages greater than 0!');
});

test('to pass valid number', () => {
const validate = validateMaxMessages('10');
expect(validate).toBe(true);
});
});
27 changes: 25 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const aws = require('aws-sdk');
const program = require('commander');
const { prompt } = require('inquirer');
const { validateUrl } = require('./helper');
const { validateMaxMessages, validateUrl } = require('./helper');
const { handle } = require('./main');
const { createClient } = require('./sqs');

Expand All @@ -28,13 +28,31 @@ const toQuestion = {
validate: validateUrl,
};

const maxQuestion = {
type: 'input',
name: 'maxMessages',
message: 'Enter the max number of messages:',
validate: validateMaxMessages,
};

const handleAction = (from, to, options) => {
const questions = [];
let copy = false;
let copiedOrMoved = 'moved';

if (!options.region) {
questions.push(regionQuestion);
}

if (options.copy) {
copy = true;
copiedOrMoved = 'copied';
}

if (!options.maxMessages) {
questions.push(maxQuestion);
}

if (!from) {
questions.push(fromQuestion);
}
Expand All @@ -45,6 +63,7 @@ const handleAction = (from, to, options) => {

prompt(questions).then(async ({
awsRegion = options.region,
maxMessages = options.maxMessages,
sourceQueueUrl = from,
targetQueueUrl = to,
}) => {
Expand All @@ -57,6 +76,8 @@ const handleAction = (from, to, options) => {
count = await handle({
sourceQueueUrl,
targetQueueUrl,
maxMessages,
copy,
sqs,
prompt,
skipPrompt: options.yes,
Expand All @@ -66,13 +87,15 @@ const handleAction = (from, to, options) => {
process.exit(1);
}

console.log(`${count} messages moved from ${sourceQueueUrl} to ${targetQueueUrl}!`);
console.log(`${count} messages ${copiedOrMoved} from ${sourceQueueUrl} to ${targetQueueUrl}!`);
});
};

program
.arguments('[from] [to]')
.option('-r, --region [value]', 'The AWS region')
.option('-m, --maxMessages [value]', 'Max number of messages')
.option('-y, --yes', 'Non interactive message moving')
.option('-c, --copy', 'Copy messages to new queue, do not delete')
.action(handleAction)
.parse(process.argv);
27 changes: 19 additions & 8 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,36 @@ const { Spinner } = require('clui');
const handle = async ({
sourceQueueUrl,
targetQueueUrl,
maxMessages,
copy,
sqs,
prompt,
skipPrompt,
}) => {
const count = await sqs.getCount(sourceQueueUrl);
await sqs.getCount(targetQueueUrl);

if (count === 0) {
if (parseInt(count, 10) === 0) {
throw new Error(`The queue ${sourceQueueUrl} is empty!`);
}

let copyOrMove = 'move';
if (copy) {
copyOrMove = 'copy';
}

const maxCount = parseInt(maxMessages, 10);
let moveCount = count;
if (count > maxCount) {
moveCount = maxCount;
}

if (!skipPrompt) {
const { move } = await prompt([
{
type: 'confirm',
name: 'move',
message: `Do you want to move ${count} messages?`,
message: `Do you want to ${copyOrMove} ${moveCount} of ${count} messages?`,
default: false,
},
]);
Expand All @@ -29,23 +42,21 @@ const handle = async ({
}
}

const spinner = new Spinner(`Moving ${count} messages...`);
const spinner = new Spinner(`Moving ${moveCount} messages...`);
spinner.start();

const promises = [];

for (let i = 0; i < count; i += 1) {
promises.push(sqs.moveMessage(sourceQueueUrl, targetQueueUrl));
}

promises.push(sqs.moveMessage(sourceQueueUrl, targetQueueUrl, copy));

await Promise.all(promises).then(() => {
spinner.stop();
}).catch((e) => {
spinner.stop();
throw new Error(e.message);
});

return count;
return moveCount;
};

module.exports = {
Expand Down
87 changes: 76 additions & 11 deletions src/main.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,97 @@
const { handle } = require('./main');

describe('handle', () => {
const sourceQueueUrl = 'https://sqs.region.amazonaws.com/123456789/srcQueue';
const targetQueueUrl = 'https://sqs.region.amazonaws.com/123456789/targetQueue';
const maxMessages = 5;
let getCountVal = 3;

test('to move messages', async () => {
const sqs = {
getCount: jest.fn(() => getCountVal),
moveMessage: jest.fn(),
};

const prompt = jest.fn(() => ({ move: true }));

await handle({
sourceQueueUrl,
targetQueueUrl,
maxMessages,
sqs,
prompt,
});

expect(sqs.getCount).toHaveBeenCalled();
expect(sqs.moveMessage).toHaveBeenCalledTimes(getCountVal);
});

test('to move messages with maxCount', async () => {
const sqs = {
getCount: jest.fn(() => getCountVal),
moveMessage: jest.fn(),
};

const prompt = jest.fn(() => ({ move: true }));
const copy = false;

await handle({
sourceQueueUrl: sourceQueueUrl,
targetQueueUrl: targetQueueUrl,
copy,
sqs,
prompt,
});

expect(sqs.getCount).toHaveBeenCalled();
expect(sqs.moveMessage).toHaveBeenCalledWith(sourceQueueUrl, targetQueueUrl, copy);
});

test('to copy messages', async () => {
const sqs = {
getCount: jest.fn(() => 3),
moveMessage: jest.fn(),
};

const prompt = jest.fn(() => ({ move: true }));
const copy = true;
getCountVal = 10;

await handle({
sourceQueueUrl: 'https://sqs.region.amazonaws.com/123456789/srcQueue',
targetQueueUrl: 'https://sqs.region.amazonaws.com/123456789/targetQueue',
sourceQueueUrl,
targetQueueUrl,
maxMessages,
copy,
sqs,
prompt,
});

expect(sqs.getCount).toHaveBeenCalled();
expect(sqs.moveMessage).toHaveBeenCalledWith(sourceQueueUrl, targetQueueUrl, copy);
expect(sqs.moveMessage).toHaveBeenCalledTimes(maxMessages);
});

test('to move messages without prompt', async () => {
const sqs = {
getCount: jest.fn(() => 3),
getCount: jest.fn(() => getCountVal),
moveMessage: jest.fn(),
};

const prompt = jest.fn();
const copy = false;

await handle({
sourceQueueUrl: 'https://sqs.region.amazonaws.com/123456789/srcQueue',
targetQueueUrl: 'https://sqs.region.amazonaws.com/123456789/targetQueue',
sourceQueueUrl,
targetQueueUrl,
maxMessages,
copy,
sqs,
prompt,
skipPrompt: true,
});

expect(sqs.getCount).toHaveBeenCalled();
expect(sqs.moveMessage).toHaveBeenCalledWith(sourceQueueUrl, targetQueueUrl, copy);
});

describe('reject promise', () => {
Expand All @@ -50,10 +106,13 @@ describe('handle', () => {
};

const prompt = jest.fn(() => ({ move: true }));
const copy = false;

expect(handle({
sourceQueueUrl: 'https://sqs.region.amazonaws.com/123456789/srcQueue',
targetQueueUrl: 'https://sqs.region.amazonaws.com/123456789/targetQueue',
sourceQueueUrl,
targetQueueUrl,
maxMessages,
copy,
sqs,
prompt,
})).rejects.toEqual({ message: 'getCount' });
Expand All @@ -66,10 +125,13 @@ describe('handle', () => {
};

const prompt = jest.fn(() => ({ move: true }));
const copy = false;

expect(handle({
sourceQueueUrl: 'https://sqs.region.amazonaws.com/123456789/srcQueue',
targetQueueUrl: 'https://sqs.region.amazonaws.com/123456789/targetQueue',
sourceQueueUrl,
targetQueueUrl,
maxMessages,
copy,
sqs,
prompt,
})).rejects.toEqual(new Error('moveMessage'));
Expand All @@ -82,10 +144,13 @@ describe('handle', () => {
};

const prompt = jest.fn(() => ({ move: true }));
const copy = false;

expect(handle({
sourceQueueUrl: 'https://sqs.region.amazonaws.com/123456789/srcQueue',
targetQueueUrl: 'https://sqs.region.amazonaws.com/123456789/targetQueue',
sourceQueueUrl,
targetQueueUrl,
maxMessages,
copy,
sqs,
prompt,
})).rejects.toEqual(new Error('The queue https://sqs.region.amazonaws.com/123456789/srcQueue is empty!'));
Expand Down
Loading