Transform code base to typescript

Change-Id: Ia1f862fb5962ddd54b8d7643abbc39bb314d1f8e
This commit is contained in:
Manuel Stahl
2024-04-22 14:23:55 +02:00
parent 03fcd8126a
commit 2466af6936
45 changed files with 1081 additions and 516 deletions

View File

@@ -1,14 +1,17 @@
import fetchMock from "jest-fetch-mock";
import authProvider from "./authProvider";
fetchMock.enableMocks();
describe("authProvider", () => {
beforeEach(() => {
fetch.resetMocks();
fetchMock.resetMocks();
localStorage.clear();
});
describe("login", () => {
it("should successfully login with username and password", async () => {
fetch.once(
fetchMock.once(
JSON.stringify({
home_server: "example.com",
user_id: "@user:example.com",
@@ -17,7 +20,7 @@ describe("authProvider", () => {
})
);
const ret = await authProvider.login({
const ret: undefined = await authProvider.login({
base_url: "http://example.com",
username: "@user:example.com",
password: "secret",
@@ -29,8 +32,8 @@ describe("authProvider", () => {
{
body: '{"device_id":null,"initial_device_display_name":"Synapse Admin","type":"m.login.password","user":"@user:example.com","password":"secret"}',
headers: new Headers({
Accept: ["application/json"],
"Content-Type": ["application/json"],
Accept: "application/json",
"Content-Type": "application/json",
}),
method: "POST",
}
@@ -43,7 +46,7 @@ describe("authProvider", () => {
});
it("should successfully login with token", async () => {
fetch.once(
fetchMock.once(
JSON.stringify({
home_server: "example.com",
user_id: "@user:example.com",
@@ -52,19 +55,19 @@ describe("authProvider", () => {
})
);
const ret = await authProvider.login({
const ret: undefined = await authProvider.login({
base_url: "https://example.com/",
loginToken: "login_token",
});
expect(ret).toBe(undefined);
expect(fetch).toBeCalledWith(
expect(fetch).toHaveBeenCalledWith(
"https://example.com/_matrix/client/r0/login",
{
body: '{"device_id":null,"initial_device_display_name":"Synapse Admin","type":"m.login.token","token":"login_token"}',
headers: new Headers({
Accept: ["application/json"],
"Content-Type": ["application/json"],
Accept: "application/json",
"Content-Type": "application/json",
}),
method: "POST",
}
@@ -79,14 +82,14 @@ describe("authProvider", () => {
it("should remove the access_token from localStorage", async () => {
localStorage.setItem("base_url", "example.com");
localStorage.setItem("access_token", "foo");
fetch.mockResponse(JSON.stringify({}));
fetchMock.mockResponse(JSON.stringify({}));
await authProvider.logout();
await authProvider.logout(null);
expect(fetch).toBeCalledWith("example.com/_matrix/client/r0/logout", {
headers: new Headers({
Accept: ["application/json"],
Authorization: ["Bearer foo"],
Accept: "application/json",
Authorization: "Bearer foo",
}),
method: "POST",
user: { authenticated: true, token: "Bearer foo" },
@@ -129,7 +132,7 @@ describe("authProvider", () => {
describe("getPermissions", () => {
it("should do nothing", async () => {
await expect(authProvider.getPermissions()).resolves.toBeUndefined();
await expect(authProvider.getPermissions(null)).resolves.toBeUndefined();
});
});
});

View File

@@ -1,10 +1,20 @@
import { fetchUtils } from "react-admin";
import { AuthProvider, Options, fetchUtils } from "react-admin";
const authProvider = {
const authProvider: AuthProvider = {
// called when the user attempts to log in
login: async ({ base_url, username, password, loginToken }) => {
login: async ({
base_url,
username,
password,
loginToken,
}: {
base_url: string;
username: string;
password: string;
loginToken: string;
}) => {
console.log("login ");
const options = {
const options: Options = {
method: "POST",
body: JSON.stringify(
Object.assign(
@@ -49,7 +59,7 @@ const authProvider = {
localStorage.getItem("base_url") + "/_matrix/client/r0/logout";
const access_token = localStorage.getItem("access_token");
const options = {
const options: Options = {
method: "POST",
user: {
authenticated: true,
@@ -63,7 +73,7 @@ const authProvider = {
}
},
// called when the API returns an error
checkError: ({ status }) => {
checkError: ({ status }: { status: number }) => {
console.log("checkError " + status);
if (status === 401 || status === 403) {
return Promise.reject();

View File

@@ -1,7 +1,10 @@
import fetchMock from "jest-fetch-mock";
import dataProvider from "./dataProvider";
fetchMock.enableMocks();
beforeEach(() => {
fetch.resetMocks();
fetchMock.resetMocks();
});
describe("dataProvider", () => {
@@ -9,7 +12,7 @@ describe("dataProvider", () => {
localStorage.setItem("access_token", "access_token");
it("fetches all users", async () => {
fetch.mockResponseOnce(
fetchMock.mockResponseOnce(
JSON.stringify({
users: [
{
@@ -48,7 +51,7 @@ describe("dataProvider", () => {
});
it("fetches one user", async () => {
fetch.mockResponseOnce(
fetchMock.mockResponseOnce(
JSON.stringify({
name: "user_id1",
password: "user_password",

View File

@@ -1,8 +1,15 @@
import { fetchUtils } from "react-admin";
import {
DataProvider,
DeleteParams,
Identifier,
Options,
RaRecord,
fetchUtils,
} from "react-admin";
import { stringify } from "query-string";
// Adds the access token to all requests
const jsonClient = (url, options = {}) => {
const jsonClient = (url: string, options: Options = {}) => {
const token = localStorage.getItem("access_token");
console.log("httpClient " + url);
if (token != null) {
@@ -14,10 +21,10 @@ const jsonClient = (url, options = {}) => {
return fetchUtils.fetchJson(url, options);
};
const mxcUrlToHttp = mxcUrl => {
const mxcUrlToHttp = (mxcUrl: string) => {
const homeserver = localStorage.getItem("base_url");
const re = /^mxc:\/\/([^/]+)\/(\w+)/;
var ret = re.exec(mxcUrl);
const ret = re.exec(mxcUrl);
console.log("mxcClient " + ret);
if (ret == null) return null;
const serverName = ret[1];
@@ -25,13 +32,188 @@ const mxcUrlToHttp = mxcUrl => {
return `${homeserver}/_matrix/media/r0/thumbnail/${serverName}/${mediaId}?width=24&height=24&method=scale`;
};
interface Room {
room_id: string;
name?: string;
canonical_alias?: string;
avatar_url?: string;
joined_members: number;
joined_local_members: number;
version: number;
creator: string;
encryption?: string;
federatable: boolean;
public: boolean;
join_rules: "public" | "knock" | "invite" | "private";
guest_access?: "can_join" | "forbidden";
history_visibility: "invited" | "joined" | "shared" | "world_readable";
state_events: number;
room_type?: string;
}
interface RoomState {
age: number;
content: {
alias?: string;
};
event_id: string;
origin_server_ts: number;
room_id: string;
sender: string;
state_key: string;
type: string;
user_id: string;
unsigned: {
age?: number;
};
}
interface ForwardExtremity {
event_id: string;
state_group: number;
depth: number;
received_ts: number;
}
interface EventReport {
id: number;
received_ts: number;
room_id: string;
name: string;
event_id: string;
user_id: string;
reason?: string;
score?: number;
sender: string;
canonical_alias?: string;
}
interface Threepid {
medium: string;
address: string;
added_at: number;
validated_at: number;
}
interface ExternalId {
auth_provider: string;
external_id: string;
}
interface User {
name: string;
displayname?: string;
threepids: Threepid[];
avatar_url?: string;
is_guest: 0 | 1;
admin: 0 | 1;
deactivated: 0 | 1;
erased: boolean;
shadow_banned: 0 | 1;
creation_ts: number;
appservice_id?: string;
consent_server_notice_sent?: string;
consent_version?: string;
consent_ts?: number;
external_ids: ExternalId[];
user_type?: string;
locked: boolean;
}
interface Device {
device_id: string;
display_name?: string;
last_seen_ip?: string;
last_seen_user_agent?: string;
last_seen_ts?: number;
user_id: string;
}
interface Connection {
ip: string;
last_seen: number;
user_agent: string;
}
interface Whois {
user_id: string;
devices: Record<
string,
{
sessions: {
connections: Connection[];
}[];
}
>;
}
interface Pusher {
app_display_name: string;
app_id: string;
data: {
url?: string;
format: string;
};
url: string;
format: string;
device_display_name: string;
profile_tag: string;
kind: string;
lang: string;
pushkey: string;
}
interface UserMedia {
created_ts: number;
last_access_ts?: number;
media_id: string;
media_length: number;
media_type: string;
quarantined_by?: string;
safe_from_quarantine: boolean;
upload_name?: string;
}
interface UserMediaStatistic {
displayname: string;
media_count: number;
media_length: number;
user_id: string;
}
interface RegistrationToken {
token: string;
uses_allowed: number;
pending: number;
completed: number;
expiry_time?: number;
}
interface RaServerNotice {
id: string;
body: string;
}
interface Destination {
destination: string;
retry_last_ts: number;
retry_interval: number;
failure_ts: number;
last_successful_stream_ordering?: number;
}
interface DestinationRoom {
room_id: string;
stream_ordering: number;
}
const resourceMap = {
users: {
path: "/_synapse/admin/v2/users",
map: u => ({
map: (u: User) => ({
...u,
id: u.name,
avatar_src: mxcUrlToHttp(u.avatar_url),
avatar_src: u.avatar_url ? mxcUrlToHttp(u.avatar_url) : undefined,
is_guest: !!u.is_guest,
admin: !!u.admin,
deactivated: !!u.deactivated,
@@ -40,14 +222,14 @@ const resourceMap = {
}),
data: "users",
total: json => json.total,
create: data => ({
create: (data: RaRecord) => ({
endpoint: `/_synapse/admin/v2/users/@${encodeURIComponent(
data.id
)}:${localStorage.getItem("home_server")}`,
body: data,
method: "PUT",
}),
delete: params => ({
delete: (params: DeleteParams) => ({
endpoint: `/_synapse/admin/v1/deactivate/${encodeURIComponent(
params.id
)}`,
@@ -57,7 +239,7 @@ const resourceMap = {
},
rooms: {
path: "/_synapse/admin/v1/rooms",
map: r => ({
map: (r: Room) => ({
...r,
id: r.room_id,
alias: r.canonical_alias,
@@ -67,36 +249,29 @@ const resourceMap = {
public: !!r.public,
}),
data: "rooms",
total: json => {
return json.total_rooms;
},
delete: params => ({
total: json => json.total_rooms,
delete: (params: DeleteParams) => ({
endpoint: `/_synapse/admin/v2/rooms/${params.id}`,
body: { block: false },
}),
},
reports: {
path: "/_synapse/admin/v1/event_reports",
map: er => ({
...er,
id: er.id,
}),
map: (er: EventReport) => ({ ...er }),
data: "event_reports",
total: json => json.total,
},
devices: {
map: d => ({
map: (d: Device) => ({
...d,
id: d.device_id,
}),
data: "devices",
total: json => {
return json.total;
},
reference: id => ({
total: json => json.total,
reference: (id: Identifier) => ({
endpoint: `/_synapse/admin/v2/users/${encodeURIComponent(id)}/devices`,
}),
delete: params => ({
delete: (params: DeleteParams) => ({
endpoint: `/_synapse/admin/v2/users/${encodeURIComponent(
params.previousData.user_id
)}/devices/${params.id}`,
@@ -104,84 +279,74 @@ const resourceMap = {
},
connections: {
path: "/_synapse/admin/v1/whois",
map: c => ({
map: (c: Whois) => ({
...c,
id: c.user_id,
}),
data: "connections",
},
room_members: {
map: m => ({
map: (m: string) => ({
id: m,
}),
reference: id => ({
reference: (id: Identifier) => ({
endpoint: `/_synapse/admin/v1/rooms/${id}/members`,
}),
data: "members",
total: json => {
return json.total;
},
total: json => json.total,
},
room_state: {
map: rs => ({
map: (rs: RoomState) => ({
...rs,
id: rs.event_id,
}),
reference: id => ({
reference: (id: Identifier) => ({
endpoint: `/_synapse/admin/v1/rooms/${id}/state`,
}),
data: "state",
total: json => {
return json.state.length;
},
total: json => json.state.length,
},
pushers: {
map: p => ({
map: (p: Pusher) => ({
...p,
id: p.pushkey,
}),
reference: id => ({
reference: (id: Identifier) => ({
endpoint: `/_synapse/admin/v1/users/${encodeURIComponent(id)}/pushers`,
}),
data: "pushers",
total: json => {
return json.total;
},
total: json => json.total,
},
joined_rooms: {
map: jr => ({
map: (jr: string) => ({
id: jr,
}),
reference: id => ({
reference: (id: Identifier) => ({
endpoint: `/_synapse/admin/v1/users/${encodeURIComponent(
id
)}/joined_rooms`,
}),
data: "joined_rooms",
total: json => {
return json.total;
},
total: json => json.total,
},
users_media: {
map: um => ({
map: (um: UserMedia) => ({
...um,
id: um.media_id,
}),
reference: id => ({
reference: (id: Identifier) => ({
endpoint: `/_synapse/admin/v1/users/${encodeURIComponent(id)}/media`,
}),
data: "media",
total: json => {
return json.total;
},
delete: params => ({
total: json => json.total,
delete: (params: DeleteParams) => ({
endpoint: `/_synapse/admin/v1/media/${localStorage.getItem(
"home_server"
)}/${params.id}`,
}),
},
delete_media: {
delete: params => ({
delete: (params: DeleteParams) => ({
endpoint: `/_synapse/admin/v1/media/${localStorage.getItem(
"home_server"
)}/delete?before_ts=${params.meta.before_ts}&size_gt=${
@@ -191,25 +356,25 @@ const resourceMap = {
}),
},
protect_media: {
map: pm => ({ id: pm.media_id }),
create: params => ({
map: (pm: UserMedia) => ({ id: pm.media_id }),
create: (params: UserMedia) => ({
endpoint: `/_synapse/admin/v1/media/protect/${params.media_id}`,
method: "POST",
}),
delete: params => ({
delete: (params: DeleteParams) => ({
endpoint: `/_synapse/admin/v1/media/unprotect/${params.id}`,
method: "POST",
}),
},
quarantine_media: {
map: qm => ({ id: qm.media_id }),
create: params => ({
map: (qm: UserMedia) => ({ id: qm.media_id }),
create: (params: UserMedia) => ({
endpoint: `/_synapse/admin/v1/media/quarantine/${localStorage.getItem(
"home_server"
)}/${params.media_id}`,
method: "POST",
}),
delete: params => ({
delete: (params: DeleteParams) => ({
endpoint: `/_synapse/admin/v1/media/unquarantine/${localStorage.getItem(
"home_server"
)}/${params.id}`,
@@ -217,8 +382,8 @@ const resourceMap = {
}),
},
servernotices: {
map: n => ({ id: n.event_id }),
create: data => ({
map: (n: { event_id: string }) => ({ id: n.event_id }),
create: (data: RaServerNotice) => ({
endpoint: "/_synapse/admin/v1/send_server_notice",
body: {
user_id: data.id,
@@ -232,50 +397,44 @@ const resourceMap = {
},
user_media_statistics: {
path: "/_synapse/admin/v1/statistics/users/media",
map: usms => ({
map: (usms: UserMediaStatistic) => ({
...usms,
id: usms.user_id,
}),
data: "users",
total: json => {
return json.total;
},
total: json => json.total,
},
forward_extremities: {
map: fe => ({
map: (fe: ForwardExtremity) => ({
...fe,
id: fe.event_id,
}),
reference: id => ({
reference: (id: Identifier) => ({
endpoint: `/_synapse/admin/v1/rooms/${id}/forward_extremities`,
}),
data: "results",
total: json => {
return json.count;
},
delete: params => ({
total: json => json.count,
delete: (params: DeleteParams) => ({
endpoint: `/_synapse/admin/v1/rooms/${params.id}/forward_extremities`,
}),
},
room_directory: {
path: "/_matrix/client/r0/publicRooms",
map: rd => ({
map: (rd: Room) => ({
...rd,
id: rd.room_id,
public: !!rd.public,
guest_access: !!rd.guest_access,
avatar_src: mxcUrlToHttp(rd.avatar_url),
avatar_src: rd.avatar_url ? mxcUrlToHttp(rd.avatar_url) : undefined,
}),
data: "chunk",
total: json => {
return json.total_room_count_estimate;
},
create: params => ({
total: json => json.total_room_count_estimate,
create: (params: RaRecord) => ({
endpoint: `/_matrix/client/r0/directory/list/room/${params.id}`,
body: { visibility: "public" },
method: "PUT",
}),
delete: params => ({
delete: (params: DeleteParams) => ({
endpoint: `/_matrix/client/r0/directory/list/room/${params.id}`,
body: { visibility: "private" },
method: "PUT",
@@ -283,54 +442,49 @@ const resourceMap = {
},
destinations: {
path: "/_synapse/admin/v1/federation/destinations",
map: dst => ({
map: (dst: Destination) => ({
...dst,
id: dst.destination,
}),
data: "destinations",
total: json => {
return json.total;
},
total: json => json.total,
delete: params => ({
endpoint: `/_synapse/admin/v1/federation/destinations/${params.id}/reset_connection`,
method: "POST",
}),
},
destination_rooms: {
map: dstroom => ({
map: (dstroom: DestinationRoom) => ({
...dstroom,
id: dstroom.room_id,
}),
reference: id => ({
reference: (id: Identifier) => ({
endpoint: `/_synapse/admin/v1/federation/destinations/${id}/rooms`,
}),
data: "rooms",
total: json => {
return json.total;
},
total: json => json.total,
},
registration_tokens: {
path: "/_synapse/admin/v1/registration_tokens",
map: rt => ({
map: (rt: RegistrationToken) => ({
...rt,
id: rt.token,
}),
data: "registration_tokens",
total: json => {
return json.registration_tokens.length;
},
create: params => ({
total: json => json.registration_tokens.length,
create: (params: RaRecord) => ({
endpoint: "/_synapse/admin/v1/registration_tokens/new",
body: params,
method: "POST",
}),
delete: params => ({
delete: (params: DeleteParams) => ({
endpoint: `/_synapse/admin/v1/registration_tokens/${params.id}`,
}),
},
};
function filterNullValues(key, value) {
/* eslint-disable @typescript-eslint/no-explicit-any */
function filterNullValues(key: string, value: any) {
// Filtering out null properties
// to reset user_type from user, it must be null
if (value === null && key !== "user_type") {
@@ -339,7 +493,7 @@ function filterNullValues(key, value) {
return value;
}
function getSearchOrder(order) {
function getSearchOrder(order: "ASC" | "DESC") {
if (order === "DESC") {
return "b";
} else {
@@ -347,7 +501,7 @@ function getSearchOrder(order) {
}
}
const dataProvider = {
const dataProvider: DataProvider = {
getList: async (resource, params) => {
console.log("getList " + resource);
const {
@@ -376,7 +530,8 @@ const dataProvider = {
dir: getSearchOrder(order),
};
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
if (!homeserver || !(resource in resourceMap))
throw Error("Homeserver not set");
const res = resourceMap[resource];
@@ -393,7 +548,8 @@ const dataProvider = {
getOne: async (resource, params) => {
console.log("getOne " + resource);
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
if (!homeserver || !(resource in resourceMap))
throw Error("Homeserver not set");
const res = resourceMap[resource];
@@ -407,7 +563,8 @@ const dataProvider = {
getMany: async (resource, params) => {
console.log("getMany " + resource);
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
if (!homeserver || !(resource in resourceMap))
throw Error("Homerserver not set");
const res = resourceMap[resource];
@@ -436,7 +593,8 @@ const dataProvider = {
};
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
if (!homeserver || !(resource in resourceMap))
throw Error("Homeserver not set");
const res = resourceMap[resource];
@@ -453,7 +611,8 @@ const dataProvider = {
update: async (resource, params) => {
console.log("update " + resource);
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
if (!homeserver || !(resource in resourceMap))
throw Error("Homeserver not set");
const res = resourceMap[resource];
@@ -471,7 +630,8 @@ const dataProvider = {
updateMany: async (resource, params) => {
console.log("updateMany " + resource);
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
if (!homeserver || !(resource in resourceMap))
throw Error("Homeserver not set");
const res = resourceMap[resource];
@@ -491,7 +651,8 @@ const dataProvider = {
create: async (resource, params) => {
console.log("create " + resource);
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
if (!homeserver || !(resource in resourceMap))
throw Error("Homeserver not set");
const res = resourceMap[resource];
if (!("create" in res)) return Promise.reject();
@@ -505,13 +666,17 @@ const dataProvider = {
return { data: res.map(json) };
},
createMany: async (resource, params) => {
createMany: async (
resource: string,
params: { ids: Identifier[]; data: RaRecord }
) => {
console.log("createMany " + resource);
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
if (!homeserver || !(resource in resourceMap))
throw Error("Homeserver not set");
const res = resourceMap[resource];
if (!("create" in res)) return Promise.reject();
if (!("create" in res)) throw Error(`Create ${resource} is not allowed`);
const responses = await Promise.all(
params.ids.map(id => {
@@ -530,7 +695,8 @@ const dataProvider = {
delete: async (resource, params) => {
console.log("delete " + resource);
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
if (!homeserver || !(resource in resourceMap))
throw Error("Homeserver not set");
const res = resourceMap[resource];
@@ -555,7 +721,8 @@ const dataProvider = {
deleteMany: async (resource, params) => {
console.log("deleteMany " + resource);
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
if (!homeserver || !(resource in resourceMap))
throw Error("Homeserver not set");
const res = resourceMap[resource];
@@ -579,7 +746,7 @@ const dataProvider = {
params.ids.map(id =>
jsonClient(`${endpoint_url}/${id}`, {
method: "DELETE",
body: JSON.stringify(params.data, filterNullValues),
// body: JSON.stringify(params.data, filterNullValues), @FIXME
})
)
);