Skip to content

Commit 3308891

Browse files
chore: update to latests Deno and CLI standards (#48)
* chore: update sample to Deno 2 standards * Fix typos * Use Deno 2 to run CI * Update .slack/.gitignore Co-authored-by: Eden Zimbelman <zim@o526.net> * Update .github/dependabot.yml Co-authored-by: Eden Zimbelman <zim@o526.net> * improve based on feedback * Update README.md * pin dependency version * improve based on feedback * Update dependabot.yml * move test_utils to the top level * move testing utilities to a better location * Update deno.jsonc * Update deno.jsonc * simplify stub fetch * Update translate_test.ts --------- Co-authored-by: Eden Zimbelman <zim@o526.net>
1 parent acd78c7 commit 3308891

File tree

12 files changed

+127
-107
lines changed

12 files changed

+127
-107
lines changed

.github/dependabot.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "github-actions"
4+
directory: "/"
5+
schedule:
6+
interval: "monthly"

.github/workflows/deno.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ name: Deno app build and testing
22

33
on:
44
push:
5-
branches: [ main ]
5+
branches:
6+
- main
67
pull_request:
7-
branches: [ main ]
8+
branches:
9+
- main
810

911
jobs:
1012
deno:
@@ -18,7 +20,7 @@ jobs:
1820
- name: Setup Deno
1921
uses: denoland/setup-deno@v1
2022
with:
21-
deno-version: v1.x
23+
deno-version: v2.x
2224

2325
- name: Verify formatting
2426
run: deno fmt --check

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
dist
22
package
33
.DS_Store
4-
.slack/apps.dev.json
54
.env

.slack/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
apps.dev.json
2+
cache/
File renamed without changes.

README.md

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,9 @@ $ slack activity --tail
198198
Contains `apps.dev.json` and `apps.json`, which include installation details for
199199
development and deployed apps.
200200

201-
### `datastores/`
202-
203-
[Datastores](https://api.slack.com/automation/datastores) securely store data
204-
for your application on Slack infrastructure. Required scopes to use datastores
205-
include `datastore:write` and `datastore:read`.
201+
Contains `hooks.json` used by the CLI to interact with the project's SDK
202+
dependencies. It contains script hooks that are executed by the CLI and
203+
implemented by the SDK.
206204

207205
### `functions/`
208206

@@ -230,11 +228,6 @@ continuing to the next step.
230228
The [app manifest](https://api.slack.com/automation/manifest) contains the app's
231229
configuration. This file defines attributes like app name and description.
232230

233-
### `slack.json`
234-
235-
Used by the CLI to interact with the project's SDK dependencies. It contains
236-
script hooks that are executed by the CLI and implemented by the SDK.
237-
238231
## Resources
239232

240233
To learn more about developing automations on Slack, visit the following:

deno.jsonc

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"$schema": "https://deno.land/x/deno/cli/schemas/config-file.v1.json",
2+
"$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json",
33
"fmt": {
44
"include": [
55
"README.md",
@@ -13,7 +13,6 @@
1313
"workflows"
1414
]
1515
},
16-
"importMap": "import_map.json",
1716
"lint": {
1817
"include": [
1918
"datastores",
@@ -28,6 +27,12 @@
2827
},
2928
"lock": false,
3029
"tasks": {
31-
"test": "deno fmt --check && deno lint && deno test --allow-read --allow-none"
30+
"test": "deno fmt --check && deno lint && deno test --allow-read"
31+
},
32+
"imports": {
33+
"deno-slack-sdk/": "https://deno.land/x/deno_slack_sdk@2.15.0/",
34+
"deno-slack-api/": "https://deno.land/x/deno_slack_api@2.8.0/",
35+
"@std/testing": "jsr:@std/testing@^1.0.12",
36+
"@std/assert": "jsr:@std/assert@^1.0.13"
3237
}
3338
}

functions/detect_lang_test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { SlackFunctionTester } from "deno-slack-sdk/mod.ts";
2-
import { assertEquals } from "std/testing/asserts.ts";
2+
import { assertEquals } from "@std/assert";
33
import handler from "./detect_lang.ts";
44

55
const { createContext } = SlackFunctionTester("my-function");

functions/translate.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DefineFunction, Schema, SlackFunction } from "deno-slack-sdk/mod.ts";
2-
import { SlackAPIClient } from "deno-slack-sdk/types.ts";
2+
import type { SlackAPIClient } from "deno-slack-sdk/types.ts";
33
import { isDebugMode } from "./internals/debug_mode.ts";
44

55
export const def = DefineFunction({
@@ -52,7 +52,7 @@ export default SlackFunction(def, async ({ inputs, client, env }) => {
5252
return { error };
5353
}
5454

55-
if (translationTargetResponse.messages.length == 0) {
55+
if (translationTargetResponse.messages.length === 0) {
5656
console.log("No message found");
5757
return emptyOutputs; // this is not an error
5858
}
@@ -81,7 +81,7 @@ export default SlackFunction(def, async ({ inputs, client, env }) => {
8181
if (match.match(/^[#@].*$/)) {
8282
const matched = match.match(/^([#@].*)$/);
8383
if (matched != null) {
84-
return "<mrkdwn>" + matched[1] + "</mrkdwn>";
84+
return `<mrkdwn>${matched[1]}</mrkdwn>`;
8585
}
8686
return "";
8787
}
@@ -93,32 +93,32 @@ export default SlackFunction(def, async ({ inputs, client, env }) => {
9393
if (match.match(/^!date.*$/)) {
9494
const matched = match.match(/^(!date.*)$/);
9595
if (matched != null) {
96-
return "<mrkdwn>" + matched[1] + "</mrkdwn>";
96+
return `<mrkdwn>${matched[1]}</mrkdwn>`;
9797
}
9898
return "";
9999
}
100100
// match special mention
101101
if (match.match(/^!.*$/)) {
102102
const matched = match.match(/^!(.*?)(?:\|.*)?$/);
103103
if (matched != null) {
104-
return "<ignore>@" + matched[1] + "</ignore>";
104+
return `<ignore>@${matched[1]}</ignore>`;
105105
}
106106
return "<ignore>@[special mention]</ignore>";
107107
}
108108
// match formatted link
109109
if (match.match(/^.*?\|.*$/)) {
110110
const matched = match.match(/^(.*?)\|(.*)$/);
111111
if (matched != null) {
112-
return '<a href="' + matched[1] + '">' + matched[2] + "</a>";
112+
return `<a href="${matched[1]}">${matched[2]}</a>`;
113113
}
114114
return "";
115115
}
116116
// fallback (raw link or unforeseen formatting)
117-
return "<mrkdwn>" + match + "</mrkdwn>";
117+
return `<mrkdwn>${match}</mrkdwn>`;
118118
// match emoji
119119
})
120120
.replace(/:([a-z0-9_-]+):/g, (_: unknown, match: string) => {
121-
return "<emoji>" + match + "</emoji>";
121+
return `<emoji>${match}</emoji>`;
122122
});
123123
body.append("text", targetText);
124124
body.append("tag_handling", "xml");
@@ -133,8 +133,8 @@ export default SlackFunction(def, async ({ inputs, client, env }) => {
133133
},
134134
body,
135135
});
136-
if (deeplResponse.status != 200) {
137-
if (deeplResponse.status == 403) {
136+
if (deeplResponse.status !== 200) {
137+
if (deeplResponse.status === 403) {
138138
// If the status code is 403, the given auth key is not valid
139139
const error =
140140
`Translating a message failed! Please make sure if the DEEPL_AUTH_KEY is correct. - (status: ${deeplResponse.status}, target text: ${
@@ -170,19 +170,19 @@ export default SlackFunction(def, async ({ inputs, client, env }) => {
170170
const translatedText = translationResult.translations[0].text
171171
// Parse encoding tags to restore the original special syntax
172172
.replace(/<emoji>([a-z0-9_-]+)<\/emoji>/g, (_: unknown, match: string) => {
173-
return ":" + match + ":";
173+
return `:${match}:`;
174174
// match <mrkdwn>...</mrkdwn>
175175
})
176176
.replace(/<mrkdwn>(.*?)<\/mrkdwn>/g, (_: unknown, match: string) => {
177-
return "<" + match + ">";
177+
return `<${match}>`;
178178
// match <a href="...">...</a>
179179
})
180180
.replace(
181181
/(<a href="(?:.*?)">(?:.*?)<\/a>)/g,
182182
(_: unknown, match: string) => {
183183
const matched = match.match(/<a href="(.*?)">(.*?)<\/a>/);
184184
if (matched != null) {
185-
return "<" + matched[1] + "|" + matched[2] + ">";
185+
return `<${matched[1]}|${matched[2]}>`;
186186
}
187187
return "";
188188
// match <ignore>...</ignore>

functions/translate_test.ts

Lines changed: 88 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,100 @@
1-
import * as mf from "mock-fetch/mod.ts";
21
import { SlackFunctionTester } from "deno-slack-sdk/mod.ts";
3-
import { assertEquals } from "std/testing/asserts.ts";
2+
import { assertEquals } from "@std/assert";
3+
import { stub } from "@std/testing/mock";
44
import handler from "./translate.ts";
55

6-
// Replaces globalThis.fetch with the mocked copy
7-
mf.install();
6+
function stubFetch() {
7+
// Replaces globalThis.fetch with the mocked copy
8+
return stub(
9+
globalThis,
10+
"fetch",
11+
async (url: string | URL | Request, options?: RequestInit) => {
12+
const req = url instanceof Request ? url : new Request(url, options);
813

9-
mf.mock("POST@/api/conversations.replies", (args) => {
10-
const authHeader = args.headers.get("Authorization");
11-
if (authHeader === "Bearer empty-response") {
12-
return new Response(JSON.stringify({ ok: true, messages: [] }), {
13-
status: 200,
14-
});
15-
}
16-
return new Response(
17-
JSON.stringify({
18-
"ok": true,
19-
"oldest": "1670566778.964519",
20-
"messages": [
21-
{
22-
"type": "message",
23-
"text":
24-
"Make work life simpler, more pleasant and more productive.\n\nSlack is the collaboration hub that brings the right people, information, and tools together to get work done. From Fortune 100 companies to corner markets, millions of people around the world use Slack to connect their teams, unify their systems, and drive their business forward.",
25-
"user": "U03E94MK0",
26-
"ts": "1670566778.964519",
27-
"team": "T03E94MJU",
28-
"thread_ts": "1670566778.964519",
29-
"reply_count": 3,
30-
"reply_users_count": 1,
31-
"latest_reply": "1670570301.090889",
32-
"reply_users": ["U04EJMQQEFN"],
33-
"is_locked": false,
34-
"subscribed": false,
35-
"reactions": [{ "name": "jp", "users": ["U03E94MK0"], "count": 1 }],
36-
},
37-
],
38-
"has_more": false,
39-
"pin_count": 0,
40-
"channel_actions_ts": null,
41-
"channel_actions_count": 0,
42-
}),
43-
{
44-
status: 200,
45-
},
46-
);
47-
});
14+
assertEquals(req.method, "POST");
4815

49-
mf.mock("POST@/api/chat.postMessage", () => {
50-
return new Response(JSON.stringify({ ok: true, ts: "1111.2222" }), {
51-
status: 200,
52-
});
53-
});
54-
55-
mf.mock("POST@/v2/translate", async (args) => {
56-
const body = await args.formData();
57-
if (body.get("auth_key") !== "valid") {
58-
return new Response("", { status: 403 });
59-
}
60-
return new Response(
61-
JSON.stringify({
62-
translations: [
63-
{
64-
detected_source_language: "EN",
65-
text:
66-
"ワークライフをよりシンプルに、より快適に、より生産的にする。\n\nSlack は、適切な人、情報、ツールを集めて仕事を成し遂げるためのコラボレーション ハブです。フォーチュン100の企業から片隅の市場ま...",
67-
},
68-
],
69-
}),
70-
{
71-
status: 200,
16+
switch (req.url) {
17+
case "https://slack.com/api/conversations.replies":
18+
if (req.headers.get("Authorization") === "Bearer empty-response") {
19+
return new Response(JSON.stringify({ ok: true, messages: [] }), {
20+
status: 200,
21+
});
22+
}
23+
return new Response(
24+
JSON.stringify({
25+
"ok": true,
26+
"oldest": "1670566778.964519",
27+
"messages": [
28+
{
29+
"type": "message",
30+
"text":
31+
"Make work life simpler, more pleasant and more productive.\n\nSlack is the collaboration hub that brings the right people, information, and tools together to get work done. From Fortune 100 companies to corner markets, millions of people around the world use Slack to connect their teams, unify their systems, and drive their business forward.",
32+
"user": "U03E94MK0",
33+
"ts": "1670566778.964519",
34+
"team": "T03E94MJU",
35+
"thread_ts": "1670566778.964519",
36+
"reply_count": 3,
37+
"reply_users_count": 1,
38+
"latest_reply": "1670570301.090889",
39+
"reply_users": ["U04EJMQQEFN"],
40+
"is_locked": false,
41+
"subscribed": false,
42+
"reactions": [{
43+
"name": "jp",
44+
"users": ["U03E94MK0"],
45+
"count": 1,
46+
}],
47+
},
48+
],
49+
"has_more": false,
50+
"pin_count": 0,
51+
"channel_actions_ts": null,
52+
"channel_actions_count": 0,
53+
}),
54+
{
55+
status: 200,
56+
},
57+
);
58+
case "https://slack.com/api/chat.postMessage": {
59+
return new Response(JSON.stringify({ ok: true, ts: "1111.2222" }), {
60+
status: 200,
61+
});
62+
}
63+
case "https://api.deepl.com/v2/translate": {
64+
const body = await req.formData();
65+
if (body.get("auth_key") === "valid") {
66+
return new Response(
67+
JSON.stringify({
68+
translations: [
69+
{
70+
detected_source_language: "EN",
71+
text:
72+
"ワークライフをよりシンプルに、より快適に、より生産的にする。\n\nSlack は、適切な人、情報、ツールを集めて仕事を成し遂げるためのコラボレーション ハブです。フォーチュン100の企業から片隅の市場ま...",
73+
},
74+
],
75+
}),
76+
{
77+
status: 200,
78+
},
79+
);
80+
}
81+
return new Response("", { status: 403 });
82+
}
83+
default:
84+
throw Error(
85+
`No stub found for ${req.method} ${req.url}\nHeaders: ${
86+
JSON.stringify(Object.fromEntries(req.headers.entries()))
87+
}`,
88+
);
89+
}
7290
},
7391
);
74-
});
92+
}
7593

7694
const { createContext } = SlackFunctionTester("my-function");
7795

7896
Deno.test("No message found", async () => {
97+
using _stubFetch = stubFetch();
7998
const inputs = {
8099
channelId: "C111",
81100
messageTs: "1670566778.964519",
@@ -88,6 +107,7 @@ Deno.test("No message found", async () => {
88107
});
89108

90109
Deno.test("Translate a message successfully", async () => {
110+
using _stubFetch = stubFetch();
91111
const inputs = {
92112
channelId: "C123",
93113
messageTs: "1670566778.964519",
@@ -100,6 +120,7 @@ Deno.test("Translate a message successfully", async () => {
100120
});
101121

102122
Deno.test("Fail to translate with an invalid auth key", async () => {
123+
using _stubFetch = stubFetch();
103124
const inputs = {
104125
channelId: "C111",
105126
messageTs: "1670566778.964519",

0 commit comments

Comments
 (0)