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

New team_id type, updates to built-ins, updated builtin function generation script instructions #327

Merged
merged 14 commits into from
Jun 10, 2024
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": [
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Flattening out deno.jsonc to remove deprecation warnings from deno output

"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: ["on_submit_function_config"],
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,16 @@ const propertiesToTypeScript = (
};

const manifestParametersToTypeScript = (
allowedHiddenParams: string[],
functionParameters: FunctionParameter[],
) => {
const typescript: string[] = [];
console.log(allowedHiddenParams, functionParameters.map((p) => p.name));
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 +135,7 @@ const manifestParametersToTypeScript = (
};

export function manifestFunctionFieldsToTypeScript(
allowedParamsMap: AllowedHiddenParamsMap,
functionRecord: FunctionRecord,
) {
const typescript: string[] = [];
Expand All @@ -111,14 +150,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 +178,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 **/
Copy link
Contributor Author

Choose a reason for hiding this comment

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

New built-in that's been public for a little bit.

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