Skip to content
This repository has been archived by the owner on Jul 24, 2019. It is now read-only.

Add typescript definition file for public API #52

Closed
Closed
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
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "1.0.1",
"description": "Slack Events API module",
"main": "dist/index.js",
"types": "types/index.d.ts",
"bin": {
"slack-verify": "dist/verify.js"
},
Expand All @@ -23,8 +24,8 @@
"prepare": "npm run build"
},
"optionalDependencies": {
"express": "^4.0.0",
"body-parser": "^1.4.3"
"body-parser": "^1.4.3",
"express": "^4.0.0"
},
"devDependencies": {
"babel-cli": "^6.18.0",
Expand Down
19 changes: 19 additions & 0 deletions types/events-api-tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Initialize using verification token from environment variables
import { createSlackEventAdapter } from '@slack/events-api';

const { PORT, SLACK_VERIFICATION_TOKEN } = { PORT: 8080, SLACK_VERIFICATION_TOKEN: 'test' };

const slackEvents = createSlackEventAdapter(SLACK_VERIFICATION_TOKEN);

// Attach listeners to events by Slack Event "type". See: https://api.slack.com/events/message.im
slackEvents.on('message', (event) => {
console.log(`Received a message event: user ${event.user} in channel ${event.channel} says ${event.text}`);
});

// Handle errors (see `errorCodes` export)
slackEvents.on('error', console.error);

// Start a basic HTTP server
slackEvents.start(PORT).then(() => {
console.log(`server listening on port ${PORT}`);
});
139 changes: 139 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Type definitions for @slack/events-api 1.0
// Project: https://github.com/@slack/events-api
// Definitions by: Tate Thurston <https://github.com/tatethurston>
// Definitions: <https://github.com/@slack/events-api>

import * as express from 'express';

export function createSlackEventAdapter(verificationToken: string, options?: SlackEventsApi.Options): SlackEventsApi.Adapter;

export namespace SlackEventsApi {
interface Options {
includeBody?: boolean;
includeHeaders?: boolean;
waitForResponse?: boolean;
}

interface Adapter {
expressMiddleware: (opts?: ExpressMiddlewareOptions) => ExpressMiddleware;
start: (port: string | number) => Promise<undefined>;
stop: () => Promise<string>;
on: OnEvent & OnError;
}

type ExpressMiddleware = (req: express.Request, res: express.Response, next: express.NextFunction) => void;

interface ExpressMiddlewareOptions {
propagateErrors?: boolean;
}

interface Event {
type: EventType;
event_ts: string;
// TODO: More constrained shape for the events
// There is conflicting information between
// https://api.slack.com/events-api#event_type_structure and individual
// events.
user?: string;
ts?: string;
item?: any;
[key: string]: any;
}

interface EventError {
code: ErrorCode;
message: string;
body: object;
}

type OnEvent = (event: EventType, Handler: EventHandler) => void;
type OnError = (event: "error", Handler: ErrorHandler) => void;

type EventHandler = (
event: Event,
body?: SlackEvent,
// TODO: More constrained types for headers and respond
headers?: any,
respond?: any
) => void;

type ErrorHandler = (error: EventError) => void;

interface SlackEvent {
token: string;
team_id: string;
api_app_id: string;
event: Event;
type: "event_callback" | "url_verification";
authed_users: string[];
event_id: string;
event_time: number;
}

type ErrorCode =
"SLACKEVENTMIDDLEWARE_NO_BODY_PARSER" |
"SLACKEVENTMIDDLEWARE_TOKEN_VERIFICATION_FAILURE";

type EventType =
"app_uninstalled" |
Copy link
Contributor

Choose a reason for hiding this comment

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

i have some apprehension to committing an exhaustive list of event types into this repo. these values are bound to change over time, and that means developers would have to deal with a compiler error if an up to date set is not available. we came up with a technique to allow flexibility using generics in this issue. i'd prefer a solution that allowed unknown event types to work without a compiler error.

Copy link
Author

Choose a reason for hiding this comment

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

Sure, if that's a goal then we could union String at the end or simply define EventType as a string.

"channel_archive" |
"channel_created" |
"channel_deleted" |
"channel_history_changed" |
"channel_rename" |
"channel_unarchive" |
"dnd_updated" |
"dnd_updated_user" |
"email_domain_changed" |
"emoji_changed" |
"file_change" |
"file_comment_added" |
"file_comment_deleted" |
"file_comment_edited" |
"file_created" |
"file_deleted" |
"file_public" |
"file_shared" |
"file_unshared" |
"grid_migration_finished" |
"grid_migration_started" |
"group_archive" |
"group_close" |
"group_history_changed" |
"group_open" |
"group_rename" |
"group_unarchive" |
"im_close" |
"im_created" |
"im_history_changed" |
"im_open" |
"link_shared" |
"member_joined_channel" |
"member_left_channel" |
"message" |
"message.channels" |
"message.groups" |
"message.im" |
"message.mpim" |
"pin_added" |
"pin_removed" |
"reaction_added" |
"reaction_removed" |
"resources_added" |
"resources_removed" |
"scope_denied" |
"scope_granted" |
"star_added" |
"star_removed" |
"subteam_created" |
"subteam_members_changed" |
"subteam_self_added" |
"subteam_self_removed" |
"subteam_updated" |
"team_domain_change" |
"team_join" |
"team_rename" |
"tokens_revoked" |
"url_verification" |
"user_change";
}
22 changes: 22 additions & 0 deletions types/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
Copy link
Contributor

Choose a reason for hiding this comment

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

its an interesting choice to essentially deliver the types in a separate package. i'm not certain what the advantages are to this approach over simply adding an index.d.ts to the root of the package. can you explain? one disadvantage i see is that its common practice to list the @types/* dependencies in the top-level package.json, see this section of the manual.

is the intention that we would publish two separate modules to npm? if so, i think that would be confusing, and at that point we might as well only offer ambient declarations via DefinitelyTyped.

Copy link
Author

Choose a reason for hiding this comment

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

Typescript's publishing documentation suggests publishing to the @types organization on npm if the package is not written in TypeScript.

I believe the intent is to avoid shipping type definitions to those who don't need them.

That said, I'm happy to instead add index.d.ts if that is your preferred approach.

"name": "@slack/events-api-types",
"version": "1.0.0",
"description": "Slack Events API Typescript definitions",
"main": "index.js",
"scripts": {
"lint": "tslint index.d.ts",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "[email protected] <[email protected]>",
"license": "ISC",
"devDependencies": {
"@types/express": "^4.11.0",
"@types/node": "^8.5.2",
"dtslint": "^0.2.0",
"tslint": "^5.8.0",
"typescript": "^2.6.2"
},
"dependencies": {
"@slack/events-api": "git://github.com/tatethurston/node-slack-events-api.git#typescript-definitions"
}
}
18 changes: 18 additions & 0 deletions types/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"baseUrl": "../../",
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"events-api-tests.ts"
]
}
1 change: 1 addition & 0 deletions types/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "extends": "dtslint/dt.json" }