Skip to content

Commit

Permalink
New team_id type, updates to built-ins, updated builtin function ge…
Browse files Browse the repository at this point in the history
…neration script instructions (#327)

* Alphabetize slack types, add team ID type

* Built in function updates:
- updated some property titles
- create-channel built in now accepts an optional team id (related to #267)
- added new Add Bookmark builtin

* model is_hidden on builtin function parameters.

* remove deprecated deno.jsonc properties.

* dont warn on hidden params, instead filter them out, but, have a special list of pre-approved hidden params we publicize anyways to account for exposing it previously
  • Loading branch information
filmaj authored Jun 10, 2024
1 parent 09d8dcd commit e5ba46f
Show file tree
Hide file tree
Showing 33 changed files with 331 additions and 130 deletions.
48 changes: 21 additions & 27 deletions deno.jsonc
Original file line number Diff line number Diff line change
@@ -1,35 +1,29 @@
{
"$schema": "https://deno.land/x/deno/cli/schemas/config-file.v1.json",
"fmt": {
"files": {
"include": [
"src",
"tests",
"docs",
"README.md",
"scripts",
".github/maintainers_guide.md",
".github/CONTRIBUTING.md"
],
"exclude": ["src/schema/slack/functions/_scripts/functions.json"]
},
"options": {
"semiColons": true,
"indentWidth": 2,
"lineWidth": 80,
"proseWrap": "always",
"singleQuote": false,
"useTabs": false
}
"include": [
"src",
"tests",
"docs",
"README.md",
"scripts",
".github/maintainers_guide.md",
".github/CONTRIBUTING.md"
],
"exclude": ["src/schema/slack/functions/_scripts/functions.json"]
"semiColons": true,
"indentWidth": 2,
"lineWidth": 80,
"proseWrap": "always",
"singleQuote": false,
"useTabs": false
},
"lint": {
"files": {
"include": ["src", "tests", "scripts"],
"exclude": [
"src/schema/slack/functions/_scripts/functions.json",
"**/*.md"
]
}
"include": ["src", "tests", "scripts"],
"exclude": [
"src/schema/slack/functions/_scripts/functions.json",
"**/*.md"
]
},
"tasks": {
"test": "deno fmt --check && deno lint && deno bundle src/mod.ts && deno test --allow-read --allow-run --parallel src/ tests/",
Expand Down
41 changes: 15 additions & 26 deletions src/schema/slack/functions/_scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,21 @@ corresponding test, the tests must be removed manually.

## Instructions

1. First, you'll need to grab a payload from `functions.list` and place the
response in a `functions.json` file inside of this `_scripts` directory.
1. First, you'll need to grab the response from `functions.categories.list` API
method tester:

- Choose a session token from a public production enterprise grid workspace that
is NOT enrolled in the `hermes_next` toggle. Recommend using the Slack DevRel
production enterprise grid token.
- Pass the `category_type=builtins_categories` parameter to this API.
- Now for the super annoying part: for each builtin category, you will need to
manually call _another_ API and assemble a functions list yourself:
- Grab the response from `functions.categories.steps.list` API method tester,
using the same session token as earlier in this step, passing each
`category_id` retrieved earlier into this API call.
- Copy the `functions` array elements from each response into a fresh
`{"functions":[]}` array in `functions.json`, slowly building up a list of
all builtin functions.

2. With this `_scripts` directory as your working directory, run the generate
script:
Expand All @@ -20,30 +33,6 @@ corresponding test, the tests must be removed manually.
> ./generate
```

3. This will output something like the following:

```txt
Cleaning folder directory
Generating code & tests for Slack function: add_pin
Generating code & tests for Slack function: add_user_to_usergroup
Generating code & tests for Slack function: archive_channel
Generating code & tests for Slack function: create_channel
Generating code & tests for Slack function: create_usergroup
Generating code & tests for Slack function: delay
Generating code & tests for Slack function: invite_user_to_channel
Generating code & tests for Slack function: open_form
Generating code & tests for Slack function: remove_user_from_usergroup
Generating code & tests for Slack function: reply_in_thread
Generating code & tests for Slack function: send_dm
Generating code & tests for Slack function: send_ephemeral_message
Generating code & tests for Slack function: send_message
Generating code & tests for Slack function: update_channel_topic
Generated 14 Slack functions with their unit tests
Updated functions module export
Formatting Slack function files...
Linting Slack function files...
```

If it completes without any linter errors, you should be good to go, with new,
formatted and linted TypeScript files for all of the Slack functions included in
your `functions.json` payload. If there are any unexpected linting issues, you
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,40 @@ import {
} from "./utils.ts";
import { AllowedTypeValue, AllowedTypeValueObject } from "./types.ts";

type AllowedHiddenParamsMap = Record<
string,
Record<"input" | "output", string[]>
>;
// Oops we accidentally exposed hidden parameters. That's ok, we'll keep them public for now.
export const allowedHiddenParams: AllowedHiddenParamsMap = {
"open_form": {
input: [],
output: ["interactivity"],
},
"reply_in_thread": {
input: ["files"],
output: ["action", "interactivity"],
},
"send_dm": {
input: ["files"],
output: [
"action",
"interactivity",
"timestamp_started",
"timestamp_completed",
],
},
"send_message": {
input: ["files"],
output: [
"action",
"interactivity",
"timestamp_started",
"timestamp_completed",
],
},
};

const typeMap: Record<string, AllowedTypeValueObject> = {
SchemaTypes,
SlackTypes: SlackSchemaTypes,
Expand Down Expand Up @@ -76,12 +110,15 @@ const propertiesToTypeScript = (
};

const manifestParametersToTypeScript = (
allowedHiddenParams: string[],
functionParameters: FunctionParameter[],
) => {
const typescript: string[] = [];
typescript.push(
`properties: {${
functionParameters.map((parameter) =>
functionParameters.filter((p) =>
allowedHiddenParams.includes(p.name) || !p.is_hidden
).map((parameter) =>
`${parameter.name}: ${propertyToTypeScript(parameter)}`
).join(",\n")
}}`,
Expand All @@ -97,6 +134,7 @@ const manifestParametersToTypeScript = (
};

export function manifestFunctionFieldsToTypeScript(
allowedParamsMap: AllowedHiddenParamsMap,
functionRecord: FunctionRecord,
) {
const typescript: string[] = [];
Expand All @@ -111,14 +149,22 @@ export function manifestFunctionFieldsToTypeScript(
`description: ${sanitize(functionRecord.description)}`,
);
}
const allowedHiddenParams = allowedParamsMap[functionRecord.callback_id] ||
{ input: [], output: [] };
typescript.push(
`input_parameters: ${
manifestParametersToTypeScript(functionRecord.input_parameters)
manifestParametersToTypeScript(
allowedHiddenParams.input,
functionRecord.input_parameters,
)
}`,
);
typescript.push(
`output_parameters: ${
manifestParametersToTypeScript(functionRecord.output_parameters)
manifestParametersToTypeScript(
allowedHiddenParams.output,
functionRecord.output_parameters,
)
}`,
);
return typescript.join(",\n");
Expand All @@ -131,7 +177,9 @@ const defineFunctionInputToTypeScript = (
typescript.push(
`callback_id: ${sanitize(getSlackCallbackId(functionRecord))}`,
);
typescript.push(manifestFunctionFieldsToTypeScript(functionRecord));
typescript.push(
manifestFunctionFieldsToTypeScript(allowedHiddenParams, functionRecord),
);
return `{${typescript.join(",\n")}}`;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ import {
renderTypeImports,
} from "./utils.ts";
import { FunctionParameter, FunctionRecord } from "../types.ts";
import { manifestFunctionFieldsToTypeScript } from "./template_function.ts";
import {
allowedHiddenParams,
manifestFunctionFieldsToTypeScript,
} from "./template_function.ts";

export const manifestFunctionToTypeScript = (
functionRecord: FunctionRecord,
) => {
return `{${manifestFunctionFieldsToTypeScript(functionRecord)}}`;
return `{${
manifestFunctionFieldsToTypeScript(allowedHiddenParams, functionRecord)
}}`;
};

const renderFunctionManifestTest = (
Expand Down
1 change: 1 addition & 0 deletions src/schema/slack/functions/_scripts/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export type FunctionProperties = {
export type FunctionParameter = FunctionProperty & {
name: string;
is_required?: boolean;
is_hidden?: boolean;
};

export type FunctionRecord = {
Expand Down
50 changes: 50 additions & 0 deletions src/schema/slack/functions/add_bookmark.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/
import { DefineFunction } from "../../../functions/mod.ts";
import SchemaTypes from "../../schema_types.ts";
import SlackTypes from "../schema_types.ts";

export default DefineFunction({
callback_id: "slack#/functions/add_bookmark",
source_file: "",
title: "Add a bookmark",
input_parameters: {
properties: {
channel_id: {
type: SlackTypes.channel_id,
description: "Search all channels",
title: "Select a channel",
},
name: {
type: SchemaTypes.string,
description: "Enter the bookmark name",
title: "Bookmark name",
},
link: {
type: SchemaTypes.string,
description: "https://docs.acme.com",
title: "Bookmark Link",
},
},
required: ["channel_id", "name", "link"],
},
output_parameters: {
properties: {
channel_id: {
type: SlackTypes.channel_id,
description: "Channel",
title: "Channel",
},
bookmark_name: {
type: SchemaTypes.string,
description: "Bookmark name",
title: "Bookmark name",
},
bookmark_link: {
type: SchemaTypes.string,
description: "Bookmark link",
title: "Bookmark link",
},
},
required: ["channel_id", "bookmark_name", "bookmark_link"],
},
});
102 changes: 102 additions & 0 deletions src/schema/slack/functions/add_bookmark_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/
import {
assertEquals,
assertExists,
assertNotStrictEquals,
} from "../../../dev_deps.ts";
import { DefineWorkflow } from "../../../workflows/mod.ts";
import { ManifestFunctionSchema } from "../../../manifest/manifest_schema.ts";
import SchemaTypes from "../../schema_types.ts";
import SlackTypes from "../schema_types.ts";
import AddBookmark from "./add_bookmark.ts";

Deno.test("AddBookmark generates valid FunctionManifest", () => {
assertEquals(
AddBookmark.definition.callback_id,
"slack#/functions/add_bookmark",
);
const expected: ManifestFunctionSchema = {
source_file: "",
title: "Add a bookmark",
input_parameters: {
properties: {
channel_id: {
type: SlackTypes.channel_id,
description: "Search all channels",
title: "Select a channel",
},
name: {
type: SchemaTypes.string,
description: "Enter the bookmark name",
title: "Bookmark name",
},
link: {
type: SchemaTypes.string,
description: "https://docs.acme.com",
title: "Bookmark Link",
},
},
required: ["channel_id", "name", "link"],
},
output_parameters: {
properties: {
channel_id: {
type: SlackTypes.channel_id,
description: "Channel",
title: "Channel",
},
bookmark_name: {
type: SchemaTypes.string,
description: "Bookmark name",
title: "Bookmark name",
},
bookmark_link: {
type: SchemaTypes.string,
description: "Bookmark link",
title: "Bookmark link",
},
},
required: ["channel_id", "bookmark_name", "bookmark_link"],
},
};
const actual = AddBookmark.export();

assertNotStrictEquals(actual, expected);
});

Deno.test("AddBookmark can be used as a Slack function in a workflow step", () => {
const testWorkflow = DefineWorkflow({
callback_id: "test_AddBookmark_slack_function",
title: "Test AddBookmark",
description: "This is a generated test to test AddBookmark",
});
testWorkflow.addStep(AddBookmark, {
channel_id: "test",
name: "test",
link: "test",
});
const actual = testWorkflow.steps[0].export();

assertEquals(actual.function_id, "slack#/functions/add_bookmark");
assertEquals(actual.inputs, {
channel_id: "test",
name: "test",
link: "test",
});
});

Deno.test("All outputs of Slack function AddBookmark should exist", () => {
const testWorkflow = DefineWorkflow({
callback_id: "test_AddBookmark_slack_function",
title: "Test AddBookmark",
description: "This is a generated test to test AddBookmark",
});
const step = testWorkflow.addStep(AddBookmark, {
channel_id: "test",
name: "test",
link: "test",
});
assertExists(step.outputs.channel_id);
assertExists(step.outputs.bookmark_name);
assertExists(step.outputs.bookmark_link);
});
Loading

0 comments on commit e5ba46f

Please sign in to comment.