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

cli-test(feat): Added datastore CLI commands #2041

Merged
merged 7 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
52 changes: 52 additions & 0 deletions packages/cli-test/src/cli/commands/datastore.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import sinon from 'sinon';

import { mockProcess } from '../../utils/test';
import { shell } from '../shell';
import datastore from './datastore';

describe('datastore commands', () => {
const sandbox = sinon.createSandbox();
let spawnSpy: sinon.SinonStub;

beforeEach(() => {
const process = mockProcess();
spawnSpy = sandbox.stub(shell, 'spawnProcess').returns({
command: 'something',
finished: true,
output: 'hi',
process,
});
sandbox.stub(shell, 'checkIfFinished').resolves();
});
afterEach(() => {
sandbox.restore();
});

describe('put method', () => {
it('should invoke `datastore put [item details]`', async () => {
await datastore.datastorePut({ appPath: '/some/path', putDetails: '{ "datastore": "datastore", "item": { "id": "1"} }' });
sandbox.assert.calledWith(
spawnSpy,
sinon.match(`datastore put '{ "datastore": "datastore", "item": { "id": "1"} }'`),
);
});
});
describe('get method', () => {
it('should invoke `datastore get <query>`', async () => {
await datastore.datastoreGet({ appPath: '/some/path', getQuery: '{ "datastore": "datastore", "id": "1" }' });
sandbox.assert.calledWith(spawnSpy, sinon.match(`datastore get '{ "datastore": "datastore", "id": "1" }'`));
});
});
describe('delete method', () => {
it('should invoke `datastore delete <query>`', async () => {
await datastore.datastoreDelete({ appPath: '/some/path', deleteQuery: '{ "datastore": "datastore", "id": "1" }' });
sandbox.assert.calledWith(spawnSpy, sinon.match(`datastore delete '{ "datastore": "datastore", "id": "1" }'`));
});
});
describe('query method', () => {
it('should invoke `datastore query [expression]`', async () => {
await datastore.datastoreQuery({ appPath: '/some/path', queryExpression: '{ "datastore": "datastore", "expression": "id = :id", "expression_values": {":id": "1"} }' });
sandbox.assert.calledWith(spawnSpy, sinon.match(`datastore query '{ "datastore": "datastore", "expression": "id = :id", "expression_values": {":id": "1"} }'`));
});
});
});
82 changes: 82 additions & 0 deletions packages/cli-test/src/cli/commands/datastore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import type { ProjectCommandArguments } from '../../types/commands/common_arguments';
import { SlackCLIProcess } from '../cli-process';

export interface DatastoreCommandArguments {
/** @description datastore get <query> */
getQuery: string;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I'm using general string for datastore commands to make it easier than pre-define attributes such as datastore, item, and expression, etc

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this will make it easy to make mistakes. I think we should model these just like we model the actual methods:

Copy link
Contributor Author

@cchensh cchensh Sep 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense, I'd say for get method, id is not a must as it's defined in the manifest as primary key. I'd like to change by adding the datastore as the new argument here.

Edit: my bad the id is always presented for get and delete command.

/** @description datastore put [item details] */
putDetails: string;
/** @description datastore query [expression] */
queryExpression: string;
/** @description datastore delete <query> */
deleteQuery: string;
}

/**
* `slack datastore put`
* @returns command output
*/
export const datastorePut = async function datastorePut(
args: ProjectCommandArguments & Pick<DatastoreCommandArguments, 'putDetails'>,
): Promise<string> {
const cmd = new SlackCLIProcess(
`datastore put '${args.putDetails}'`,
args,
);
const proc = await cmd.execAsync({
cwd: args.appPath,
});
return proc.output;
};

/**
* `slack datastore get`
* @returns command output
*/
export const datastoreGet = async function datastoreGet(
args: ProjectCommandArguments & Pick<DatastoreCommandArguments, 'getQuery'>,
): Promise<string> {
const cmd = new SlackCLIProcess(`datastore get '${args.getQuery}'`, args);
const proc = await cmd.execAsync({
cwd: args.appPath,
});
return proc.output;
};

/**
* `slack datastore delete`
* @returns command output
*/
export const datastoreDelete = async function datastoreDelete(
args: ProjectCommandArguments & Pick<DatastoreCommandArguments, 'deleteQuery'>,
): Promise<string> {
const cmd = new SlackCLIProcess(`datastore delete '${args.deleteQuery}'`, args);
const proc = await cmd.execAsync({
cwd: args.appPath,
});
return proc.output;
};

/**
* `slack datastore query`
* @returns command output
*/
export const datastoreQuery = async function datastoreQuery(
args: ProjectCommandArguments & Pick<DatastoreCommandArguments, 'queryExpression'>,
): Promise<string> {
const cmd = new SlackCLIProcess(
`datastore query '${args.queryExpression}'`,
args,
);
const proc = await cmd.execAsync({
cwd: args.appPath,
});
return proc.output;
};

export default {
datastorePut,
datastoreGet,
datastoreDelete,
datastoreQuery,
};