From 9de3b2f4fd79cbfc2bf23b6caa74c44d931877df Mon Sep 17 00:00:00 2001 From: Priyank Patel Date: Tue, 3 Aug 2021 16:26:39 +0000 Subject: [PATCH] ts: Convert user_groups module to TypeScript. --- .../node_tests/composebox_typeahead.js | 6 +- frontend_tests/node_tests/user_groups.js | 15 +++-- static/js/types.ts | 14 +++++ static/js/{user_groups.js => user_groups.ts} | 60 +++++++++++++------ tools/test-js-with-node | 1 + 5 files changed, 71 insertions(+), 25 deletions(-) create mode 100644 static/js/types.ts rename static/js/{user_groups.js => user_groups.ts} (55%) diff --git a/frontend_tests/node_tests/composebox_typeahead.js b/frontend_tests/node_tests/composebox_typeahead.js index ca66f71fd5..0d3efb8e26 100644 --- a/frontend_tests/node_tests/composebox_typeahead.js +++ b/frontend_tests/node_tests/composebox_typeahead.js @@ -256,21 +256,21 @@ const hamletcharacters = { name: "hamletcharacters", id: 1, description: "Characters of Hamlet", - members: [100, 104], + members: new Set([100, 104]), }; const backend = { name: "Backend", id: 2, description: "Backend team", - members: [], + members: new Set([]), }; const call_center = { name: "Call Center", id: 3, description: "folks working in support", - members: [], + members: new Set([]), }; const make_emoji = (emoji_dict) => ({ diff --git a/frontend_tests/node_tests/user_groups.js b/frontend_tests/node_tests/user_groups.js index 5084c085ad..20e63415d7 100644 --- a/frontend_tests/node_tests/user_groups.js +++ b/frontend_tests/node_tests/user_groups.js @@ -10,9 +10,10 @@ const user_groups = zrequire("user_groups"); run_test("user_groups", () => { const students = { + description: "Students group", name: "Students", id: 0, - members: [1, 2], + members: new Set([1, 2]), }; const params = {}; @@ -21,22 +22,22 @@ run_test("user_groups", () => { const user_id_part_of_a_group = 2; user_groups.initialize(params); - assert.equal(user_groups.get_user_group_from_id(students.id), students); + assert.deepEqual(user_groups.get_user_group_from_id(students.id), students); const admins = { name: "Admins", description: "foo", id: 1, - members: [3], + members: new Set([3]), }; const all = { name: "Everyone", id: 2, - members: [1, 2, 3], + members: new Set([1, 2, 3]), }; user_groups.add(admins); - assert.equal(user_groups.get_user_group_from_id(admins.id), admins); + assert.deepEqual(user_groups.get_user_group_from_id(admins.id), admins); const update_name_event = { group_id: admins.id, @@ -104,4 +105,8 @@ run_test("user_groups", () => { blueslip.expect("error", "Could not find user group with ID -1"); assert.equal(user_groups.is_member_of(-1, 15), false); + + blueslip.expect("error", "Could not find user group with ID -9999", 2); + user_groups.add_members(-9999); + user_groups.remove_members(-9999); }); diff --git a/static/js/types.ts b/static/js/types.ts new file mode 100644 index 0000000000..4e84363aee --- /dev/null +++ b/static/js/types.ts @@ -0,0 +1,14 @@ +// TODO/typescript: Move this to server_events_dispatch +export type UserGroupUpdateEvent = { + id: number; + type: string; + group_id: number; + data: { + name?: string; + description?: string; + }; +}; + +// TODO/typescript: Move the User and Stream placeholder +// types to their approriate modules. +export type User = Record; diff --git a/static/js/user_groups.js b/static/js/user_groups.ts similarity index 55% rename from static/js/user_groups.js rename to static/js/user_groups.ts index 359f98ff63..22f40d092e 100644 --- a/static/js/user_groups.js +++ b/static/js/user_groups.ts @@ -1,32 +1,50 @@ import * as blueslip from "./blueslip"; import {FoldDict} from "./fold_dict"; +import type {User, UserGroupUpdateEvent} from "./types"; -let user_group_name_dict; -let user_group_by_id_dict; +type UserGroup = { + description: string; + id: number; + name: string; + members: Set; +}; + +// The members field is a number array which we convert +// to a Set in the initialize function. +type UserGroupRaw = Omit & {members: number[]}; + +let user_group_name_dict: FoldDict; +let user_group_by_id_dict: Map; // We have an init() function so that our automated tests // can easily clear data. -export function init() { +export function init(): void { user_group_name_dict = new FoldDict(); - user_group_by_id_dict = new Map(); + user_group_by_id_dict = new Map(); } // WE INITIALIZE DATA STRUCTURES HERE! init(); -export function add(user_group) { +export function add(user_group_raw: UserGroupRaw): void { // Reformat the user group members structure to be a set. - user_group.members = new Set(user_group.members); + const user_group = { + description: user_group_raw.description, + id: user_group_raw.id, + name: user_group_raw.name, + members: new Set(user_group_raw.members), + }; + user_group_name_dict.set(user_group.name, user_group); user_group_by_id_dict.set(user_group.id, user_group); } -export function remove(user_group) { +export function remove(user_group: UserGroup): void { user_group_name_dict.delete(user_group.name); user_group_by_id_dict.delete(user_group.id); } -export function get_user_group_from_id(group_id) { +export function get_user_group_from_id(group_id: number): UserGroup { const user_group = user_group_by_id_dict.get(group_id); if (!user_group) { throw new Error("Unknown group_id in get_user_group_from_id: " + group_id); @@ -34,7 +52,7 @@ export function get_user_group_from_id(group_id) { return user_group; } -export function update(event) { +export function update(event: UserGroupUpdateEvent): void { const group = get_user_group_from_id(event.group_id); if (event.data.name !== undefined) { group.name = event.data.name; @@ -48,16 +66,16 @@ export function update(event) { } } -export function get_user_group_from_name(name) { +export function get_user_group_from_name(name: string): UserGroup | undefined { return user_group_name_dict.get(name); } -export function get_realm_user_groups() { +export function get_realm_user_groups(): UserGroup[] { const user_groups = Array.from(user_group_by_id_dict.values()).sort((a, b) => a.id - b.id); return user_groups.filter((group) => !group.is_system_group); } -export function is_member_of(user_group_id, user_id) { +export function is_member_of(user_group_id: number, user_id: number): boolean { const user_group = user_group_by_id_dict.get(user_group_id); if (user_group === undefined) { blueslip.error("Could not find user group with ID " + user_group_id); @@ -66,33 +84,41 @@ export function is_member_of(user_group_id, user_id) { return user_group.members.has(user_id); } -export function add_members(user_group_id, user_ids) { +export function add_members(user_group_id: number, user_ids: number[]): void { const user_group = user_group_by_id_dict.get(user_group_id); + if (user_group === undefined) { + blueslip.error("Could not find user group with ID " + user_group_id); + return; + } for (const user_id of user_ids) { user_group.members.add(user_id); } } -export function remove_members(user_group_id, user_ids) { +export function remove_members(user_group_id: number, user_ids: number[]): void { const user_group = user_group_by_id_dict.get(user_group_id); + if (user_group === undefined) { + blueslip.error("Could not find user group with ID " + user_group_id); + return; + } for (const user_id of user_ids) { user_group.members.delete(user_id); } } -export function initialize(params) { +export function initialize(params: {realm_user_groups: UserGroupRaw[]}): void { for (const user_group of params.realm_user_groups) { add(user_group); } } -export function is_user_group(item) { +export function is_user_group(item: User | UserGroup): item is UserGroup { return item.members !== undefined; } -export function get_user_groups_of_user(user_id) { +export function get_user_groups_of_user(user_id: number): UserGroup[] { const user_groups_realm = get_realm_user_groups(); const groups_of_user = user_groups_realm.filter((group) => is_member_of(group.id, user_id)); return groups_of_user; diff --git a/tools/test-js-with-node b/tools/test-js-with-node index a39bb5d76d..3ce972364b 100755 --- a/tools/test-js-with-node +++ b/tools/test-js-with-node @@ -168,6 +168,7 @@ EXEMPT_FILES = { "static/js/topic_list.js", "static/js/topic_zoom.js", "static/js/tutorial.js", + "static/js/types.ts", "static/js/typing_events.js", "static/js/typing.js", "static/js/ui_init.js",