Skip to content

Commit

Permalink
fix: register fragment migration, attach parser/target to context, mo…
Browse files Browse the repository at this point in the history
…ve ctrl reply to method
  • Loading branch information
ssube committed Dec 14, 2018
1 parent 5976d9f commit ceb375e
Show file tree
Hide file tree
Showing 14 changed files with 123 additions and 61 deletions.
2 changes: 1 addition & 1 deletion docs/parser/args-parser-complete.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ data:
- key: body
operator: every
values:
- string: "!!c"
- regexp: !regexp /!!c/
12 changes: 10 additions & 2 deletions docs/style.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This document covers Typescript and YAML style, explains some lint rules, and ma
- [Typescript](#typescript)
- [Arrays](#arrays)
- [Arrow Functions ("lambdas")](#arrow-functions-%22lambdas%22)
- [Async](#async)
- [Constructors](#constructors)
- [Destructuring](#destructuring)
- [Entities](#entities)
Expand All @@ -27,7 +28,7 @@ This document covers Typescript and YAML style, explains some lint rules, and ma
- [Return Types](#return-types)
- [Ternaries](#ternaries)
- [Tests](#tests)
- [Async](#async)
- [Async Tests](#async-tests)
- [Assertions](#assertions)
- [YAML](#yaml)

Expand Down Expand Up @@ -111,6 +112,13 @@ If the body is a single statement, a function call, or otherwise fits well on a
If the body returns an object literal or needs more than one line (excluding nested object literals), braces
MUST be used.

### Async

Async code MUST use promises. Callbacks MUST be wrapped to create and resolve (or reject) a promise.

Functions returning a promise SHOULD be `async` and `await` MAY be used inside them, but MAY also return plain promises
when no `await` is needed.

### Constructors

Classes should have a constructor if it contains more than a `super(options)` call.
Expand Down Expand Up @@ -198,7 +206,7 @@ Ternaries MUST NOT be nested.

Typescript tests (small, unit tests) are run using Mocha and Chai.

#### Async
#### Async Tests

Wrap any tests using async resources (promises, observables, the bot, services, pretty much anything) in the
`describeAsync` and `itAsync` helpers. These will track and report leaking async resources.
Expand Down
102 changes: 57 additions & 45 deletions src/controller/AuthController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import { Token } from 'src/entity/auth/Token';
import { User } from 'src/entity/auth/User';
import { UserRepository } from 'src/entity/auth/UserRepository';
import { Command, CommandVerb } from 'src/entity/Command';
import { Message } from 'src/entity/Message';
import { TYPE_JSON, TYPE_TEXT } from 'src/utils/Mime';
import { InvalidArgumentError } from 'src/error/InvalidArgumentError';

import { BaseController } from './BaseController';
import { NOUN_FRAGMENT } from './CompletionController';
Expand Down Expand Up @@ -62,7 +61,7 @@ export class AuthController extends BaseController<AuthControllerData> implement
case NOUN_USER:
return this.handleUser(cmd);
default:
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, `unsupported noun: ${cmd.noun}`));
return this.reply(cmd.context, `unsupported noun: ${cmd.noun}`);
}
}

Expand All @@ -73,7 +72,7 @@ export class AuthController extends BaseController<AuthControllerData> implement
case CommandVerb.List:
return this.listPermissions(cmd);
default:
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, `unsupported verb: ${cmd.verb}`));
return this.reply(cmd.context, `unsupported verb: ${cmd.verb}`);
}
}

Expand All @@ -86,7 +85,7 @@ export class AuthController extends BaseController<AuthControllerData> implement
case CommandVerb.List:
return this.listRoles(cmd);
default:
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, `unsupported verb: ${cmd.verb}`));
return this.reply(cmd.context, `unsupported verb: ${cmd.verb}`);
}
}

Expand All @@ -97,7 +96,7 @@ export class AuthController extends BaseController<AuthControllerData> implement
case CommandVerb.Get:
return this.getSession(cmd);
default:
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, `unsupported verb: ${cmd.verb}`));
return this.reply(cmd.context, `unsupported verb: ${cmd.verb}`);
}
}

Expand All @@ -112,7 +111,7 @@ export class AuthController extends BaseController<AuthControllerData> implement
case CommandVerb.List:
return this.listTokens(cmd);
default:
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, `unsupported verb: ${cmd.verb}`));
return this.reply(cmd.context, `unsupported verb: ${cmd.verb}`);
}
}

Expand All @@ -125,7 +124,7 @@ export class AuthController extends BaseController<AuthControllerData> implement
case CommandVerb.Update:
return this.updateUser(cmd);
default:
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, `unsupported verb: ${cmd.verb}`));
return this.reply(cmd.context, `unsupported verb: ${cmd.verb}`);
}
}

Expand All @@ -134,15 +133,15 @@ export class AuthController extends BaseController<AuthControllerData> implement
const results = permissions.map((p) => {
return `${p}: \`${cmd.context.checkGrants([p])}\``;
}).join('\n');
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, results));
return this.reply(cmd.context, results);
}

public async listPermissions(cmd: Command): Promise<void> {
const permissions = cmd.get('permissions');
const results = permissions.map((p) => {
return `${p}: \`${cmd.context.listGrants([p])}\``;
}).join('\n');
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, results));
return this.reply(cmd.context, results);
}

public async createRole(cmd: Command): Promise<void> {
Expand All @@ -152,7 +151,7 @@ export class AuthController extends BaseController<AuthControllerData> implement
grants,
name,
});
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, JSON.stringify(role)));
return this.reply(cmd.context, role.toString());
}

public async getRole(cmd: Command): Promise<void> {
Expand All @@ -162,18 +161,22 @@ export class AuthController extends BaseController<AuthControllerData> implement
name,
},
});
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, JSON.stringify(role)));
if (role) {
return this.reply(cmd.context, role.toString());
} else {
return this.reply(cmd.context, 'role not found');
}
}

public async listRoles(cmd: Command): Promise<void> {
const roles = await this.roleRepository.createQueryBuilder('role').getMany();
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, JSON.stringify(roles)));
const roleText = roles.map((r) => r.toString()).join('\n');
return this.reply(cmd.context, roleText);
}

public async createToken(cmd: Command): Promise<void> {
if (!cmd.context.user) {
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, MSG_SESSION_REQUIRED));
return;
return this.reply(cmd.context, MSG_SESSION_REQUIRED);
}

const grants = cmd.getOrDefault('grants', []);
Expand All @@ -191,35 +194,27 @@ export class AuthController extends BaseController<AuthControllerData> implement
}));
const jwt = token.sign(this.data.token.secret);

await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, JSON.stringify(token)));
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, jwt));
await this.reply(cmd.context, token.toString());
return this.reply(cmd.context, jwt);
}

public async deleteTokens(cmd: Command): Promise<void> {
if (!cmd.context.user) {
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, MSG_SESSION_REQUIRED));
return;
return this.reply(cmd.context, MSG_SESSION_REQUIRED);
}

if (cmd.getHeadOrDefault('confirm', 'no') !== 'yes') {
await this.bot.emitCommand(new Command({
context: cmd.context,
data: {},
labels: {},
noun: NOUN_FRAGMENT,
verb: CommandVerb.Create,
}));
return;
return this.requestCompletion(cmd, 'confirm', `please confirm deleting all tokens for ${cmd.context.user.id}`);
}

const results = await this.tokenRepository.delete({
user: Equal(cmd.context.user),
});

if (results.affected) {
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, `deleted ${results.affected} tokens`));
return this.reply(cmd.context, `deleted ${results.affected} tokens`);
} else {
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, `no tokens deleted`));
return this.reply(cmd.context, `no tokens deleted`);
}
}

Expand All @@ -230,24 +225,22 @@ export class AuthController extends BaseController<AuthControllerData> implement
audience: this.data.token.audience,
issuer: this.data.token.issuer,
});
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, JSON.stringify(data)));
return this.reply(cmd.context, JSON.stringify(data));
} catch (err) {
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, `error verifying token: ${err.message}`));
return this.reply(cmd.context, `error verifying token: ${err.message}`);
}
} else {
if (!cmd.context.token) {
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, 'session must be provided by a token'));
return;
return this.reply(cmd.context, 'session must be provided by a token');
} else {
return this.reply(cmd.context, cmd.context.token.toString());
}

await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, JSON.stringify(cmd.context.token)));
}
}

public async listTokens(cmd: Command): Promise<void> {
if (!cmd.context.user) {
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, MSG_SESSION_REQUIRED));
return;
return this.reply(cmd.context, MSG_SESSION_REQUIRED);
}

const tokens = await this.tokenRepository.find({
Expand All @@ -256,7 +249,7 @@ export class AuthController extends BaseController<AuthControllerData> implement
},
});

await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, JSON.stringify(tokens)));
return this.reply(cmd.context, JSON.stringify(tokens));
}

public async createUser(cmd: Command): Promise<void> {
Expand All @@ -277,7 +270,7 @@ export class AuthController extends BaseController<AuthControllerData> implement
}));
this.logger.debug({ user }, 'created user');

await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, `created user: ${user.id}`));
return this.reply(cmd.context, user.toString());
}

public async getUser(cmd: Command): Promise<void> {
Expand All @@ -288,7 +281,7 @@ export class AuthController extends BaseController<AuthControllerData> implement
},
});
await this.userRepository.loadRoles(user);
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_JSON, JSON.stringify(user)));
return this.reply(cmd.context, user.toString());
}

public async updateUser(cmd: Command): Promise<void> {
Expand All @@ -307,7 +300,7 @@ export class AuthController extends BaseController<AuthControllerData> implement
});
user.roles = roles;
const updatedUser = await this.userRepository.save(user);
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, JSON.stringify(updatedUser)));
return this.reply(cmd.context, updatedUser.toString());
}

public async createSession(cmd: Command): Promise<void> {
Expand All @@ -320,16 +313,35 @@ export class AuthController extends BaseController<AuthControllerData> implement

const session = await cmd.context.source.createSession(cmd.context.uid, user);
this.logger.debug({ session, user, userName: name }, 'created session');
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, 'created session'));
return this.reply(cmd.context, 'created session');
}

public async getSession(cmd: Command): Promise<void> {
const session = cmd.context.source.getSession(cmd.context.uid);
if (isNil(session)) {
await this.bot.sendMessage(Message.reply(cmd.context, TYPE_TEXT, 'cannot get sessions unless logged in'));
return;
return this.reply(cmd.context, 'cannot get sessions unless logged in');
}

return this.reply(cmd.context, session.toString());
}

protected async requestCompletion(cmd: Command, key: string, msg: string): Promise<void> {
if (!cmd.context.parser) {
throw new InvalidArgumentError('command has no parser to prompt for completion');
}

await this.bot.sendMessage(Message.reply(cmd.context, TYPE_JSON, session.toString()));
await this.bot.emitCommand(new Command({
context: cmd.context,
data: {
key: [key],
msg: [msg],
noun: [cmd.noun],
parser: [cmd.context.parser.id],
verb: [cmd.verb],
},
labels: {},
noun: NOUN_FRAGMENT,
verb: CommandVerb.Create,
}));
}
}
9 changes: 8 additions & 1 deletion src/controller/BaseController.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { ChildService } from 'src/ChildService';
import { Controller, ControllerData, ControllerOptions } from 'src/controller/Controller';
import { Command } from 'src/entity/Command';
import { Command, CommandVerb } from 'src/entity/Command';
import { Context } from 'src/entity/Context';
import { Message } from 'src/entity/Message';
import { InvalidArgumentError } from 'src/error/InvalidArgumentError';
import { checkFilter, Filter } from 'src/filter/Filter';
import { ServiceModule } from 'src/module/ServiceModule';
import { getLogInfo, ServiceDefinition } from 'src/Service';
import { Transform, TransformData } from 'src/transform/Transform';
import { TYPE_TEXT } from 'src/utils/Mime';

export type BaseControllerOptions<TData extends ControllerData> = ControllerOptions<TData>;

Expand Down Expand Up @@ -80,4 +83,8 @@ export abstract class BaseController<TData extends ControllerData> extends Child
}
return batch;
}

protected async reply(ctx: Context, body: string): Promise<void> {
await this.bot.sendMessage(Message.reply(ctx, TYPE_TEXT, body));
}
}
2 changes: 1 addition & 1 deletion src/controller/CompletionController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const NOUN_FRAGMENT = 'fragment';
export type CompletionControllerData = any;
export type CompletionControllerOptions = ControllerOptions<CompletionControllerData>;

@Inject('storage')
@Inject('bot', 'services', 'storage')
export class CompletionController extends BaseController<CompletionControllerData> implements Controller {
protected storage: Connection;
protected fragmentRepository: Repository<Fragment>;
Expand Down
12 changes: 12 additions & 0 deletions src/entity/Context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ export interface ContextData {
*/
name: string;

parser?: Parser;

source: Listener;

target?: Listener;

token?: Token;

/**
Expand Down Expand Up @@ -73,7 +77,9 @@ export class Context implements ContextData {
thread: options.channel.thread,
};
this.name = options.name;
this.parser = options.parser;
this.source = options.source;
this.target = options.target;
this.token = options.token;
this.uid = options.uid;
this.user = options.user;
Expand All @@ -82,6 +88,12 @@ export class Context implements ContextData {

public extend(options: Partial<ContextData>): Context {
const ctx = new Context(this);
if (options.parser) {
ctx.parser = options.parser;
}
if (options.target) {
ctx.target = options.target;
}
if (options.token) {
ctx.token = options.token;
}
Expand Down
Loading

0 comments on commit ceb375e

Please sign in to comment.