Apply strictNullChecks to src/utils/*!exportUtils (#10455

* Apply `strictNullChecks` to `src/utils/exportUtils`

* strict fix

* fix strictNullChecks issues in some utils

* fix error message

* test coverage

* lint

* more strictNullChecks

* small optimisation for getUniqueRoomsWithIndividuals

* tidy

* test coverage
This commit is contained in:
Kerry 2023-04-03 20:26:55 +12:00 committed by GitHub
parent 4ed6e39067
commit 81a4498a8f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 143 additions and 81 deletions

View file

@ -20,7 +20,6 @@ import { logger } from "matrix-js-sdk/src/logger";
import { IClientWellKnown } from "matrix-js-sdk/src/matrix";
import { _t, UserFriendlyError } from "../languageHandler";
import { makeType } from "./TypeUtils";
import SdkConfig from "../SdkConfig";
import { ValidatedServerConfig } from "./ValidatedServerConfig";
@ -43,7 +42,7 @@ export default class AutoDiscoveryUtils {
* @param {string | Error} error The error to check
* @returns {boolean} True if the error is a liveliness error.
*/
public static isLivelinessError(error: string | Error): boolean {
public static isLivelinessError(error?: string | Error | null): boolean {
if (!error) return false;
return !!LIVELINESS_DISCOVERY_ERRORS.find((e) =>
typeof error === "string" ? e === error : e === error.message,
@ -197,7 +196,7 @@ export default class AutoDiscoveryUtils {
): ValidatedServerConfig {
if (!discoveryResult || !discoveryResult["m.homeserver"]) {
// This shouldn't happen without major misconfiguration, so we'll log a bit of information
// in the log so we can find this bit of codee but otherwise tell teh user "it broke".
// in the log so we can find this bit of code but otherwise tell the user "it broke".
logger.error("Ended up in a state of not knowing which homeserver to connect to.");
throw new UserFriendlyError("Unexpected error resolving homeserver configuration");
}
@ -216,7 +215,7 @@ export default class AutoDiscoveryUtils {
// of Element.
let preferredIdentityUrl = defaultConfig && defaultConfig["isUrl"];
if (isResult && isResult.state === AutoDiscovery.SUCCESS) {
preferredIdentityUrl = isResult["base_url"];
preferredIdentityUrl = isResult["base_url"] ?? undefined;
} else if (isResult && isResult.state !== AutoDiscovery.PROMPT) {
logger.error("Error determining preferred identity server URL:", isResult);
if (isResult.state === AutoDiscovery.FAIL_ERROR) {
@ -244,6 +243,12 @@ export default class AutoDiscoveryUtils {
}
const preferredHomeserverUrl = hsResult["base_url"];
if (!preferredHomeserverUrl) {
logger.error("No homeserver URL configured");
throw new UserFriendlyError("Unexpected error resolving homeserver configuration");
}
let preferredHomeserverName = serverName ? serverName : hsResult["server_name"];
const url = new URL(preferredHomeserverUrl);
@ -255,7 +260,7 @@ export default class AutoDiscoveryUtils {
throw new UserFriendlyError("Unexpected error resolving homeserver configuration");
}
return makeType(ValidatedServerConfig, {
return {
hsUrl: preferredHomeserverUrl,
hsName: preferredHomeserverName,
hsNameIsDifferent: url.hostname !== preferredHomeserverName,
@ -263,6 +268,6 @@ export default class AutoDiscoveryUtils {
isDefault: false,
warning: hsResult.error,
isNameResolvable: !isSynthetic,
});
} as ValidatedServerConfig;
}
}

View file

@ -103,8 +103,6 @@ export default class DMRoomMap {
}
private onAccountData = (ev: MatrixEvent): void => {
console.log("onAccountData");
if (ev.getType() == EventType.Direct) {
this.setMDirectFromContent(ev.getContent());
this.userToRooms = null;
@ -207,13 +205,16 @@ export default class DMRoomMap {
public getUniqueRoomsWithIndividuals(): { [userId: string]: Room } {
if (!this.roomToUser) return {}; // No rooms means no map.
return Object.keys(this.roomToUser)
.map((r) => ({ userId: this.getUserIdForRoomId(r), room: this.matrixClient.getRoom(r) }))
.filter((r) => r.userId && r.room?.getInvitedAndJoinedMemberCount() === 2)
.reduce((obj, r) => {
obj[r.userId] = r.room;
return obj;
}, {} as Record<string, Room>);
// map roomToUser to valid rooms with two participants
return Object.keys(this.roomToUser).reduce((acc, roomId: string) => {
const userId = this.getUserIdForRoomId(roomId);
const room = this.matrixClient.getRoom(roomId);
const hasTwoMembers = room?.getInvitedAndJoinedMemberCount() === 2;
if (userId && room && hasTwoMembers) {
acc[userId] = room;
}
return acc;
}, {} as Record<string, Room>);
}
/**
@ -236,9 +237,7 @@ export default class DMRoomMap {
// to avoid multiple devices fighting to correct
// the account data, only try to send the corrected
// version once.
logger.warn(
`Invalid m.direct account data detected ` + `(self-chats that shouldn't be), patching it up.`,
);
logger.warn(`Invalid m.direct account data detected (self-chats that shouldn't be), patching it up.`);
if (neededPatching && !this.hasSentOutPatchDirectAccountDataPatch) {
this.hasSentOutPatchDirectAccountDataPatch = true;
this.matrixClient.setAccountData(EventType.Direct, userToRooms);

View file

@ -21,8 +21,8 @@ import SdkConfig from "../SdkConfig";
import { MatrixClientPeg } from "../MatrixClientPeg";
import { Policies } from "../Terms";
export function getDefaultIdentityServerUrl(): string {
return SdkConfig.get("validated_server_config").isUrl;
export function getDefaultIdentityServerUrl(): string | undefined {
return SdkConfig.get("validated_server_config")?.isUrl;
}
export function setToDefaultIdentityServer(): void {

View file

@ -102,7 +102,10 @@ export class MediaEventHelper implements IDestroyable {
}
}
return fetch(this.media.thumbnailHttp).then((r) => r.blob());
const thumbnailHttp = this.media.thumbnailHttp;
if (!thumbnailHttp) return Promise.resolve(null);
return fetch(thumbnailHttp).then((r) => r.blob());
};
public static isEligible(event: MatrixEvent): boolean {

View file

@ -1,30 +0,0 @@
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* Creates a class of a given type using the objects defined. This
* is a stopgap function while we don't have TypeScript interfaces.
* In future, we'd define the `type` as an interface and just cast
* it instead of cheating like we are here.
* @param {Type} Type The type of class to construct.
* @param {*} opts The options (properties) to set on the object.
* @returns {*} The created object.
*/
export function makeType<T>(Type: { new (): T }, opts: Partial<T>): T {
const c = new Type();
Object.assign(c, opts);
return c;
}

View file

@ -24,7 +24,7 @@ type FunctionWithUIA<R, A> = (auth?: IAuthData, ...args: A[]) => Promise<UIAResp
export function wrapRequestWithDialog<R, A = any>(
requestFunction: FunctionWithUIA<R, A>,
opts: Omit<InteractiveAuthDialogProps, "makeRequest" | "onFinished">,
opts: Omit<InteractiveAuthDialogProps<R>, "makeRequest" | "onFinished">,
): (...args: A[]) => Promise<R> {
return async function (...args): Promise<R> {
return new Promise((resolve, reject) => {

View file

@ -119,6 +119,7 @@ export default class WidgetUtils {
if (
testUrl.protocol === scalarUrl.protocol &&
testUrl.host === scalarUrl.host &&
scalarUrl.pathname &&
testUrl.pathname?.startsWith(scalarUrl.pathname)
) {
return true;

View file

@ -61,6 +61,12 @@ export async function leaveRoomBehaviour(roomId: string, retry = true, spinner =
}
const room = cli.getRoom(roomId);
// should not encounter this
if (!room) {
throw new Error(`Expected to find room for id ${roomId}`);
}
// await any queued messages being sent so that they do not fail
await Promise.all(
room

View file

@ -85,12 +85,14 @@ export const createMapSiteLinkFromEvent = (event: MatrixEvent): string | null =>
if (mLocation !== undefined) {
const uri = mLocation["uri"];
if (uri !== undefined) {
return makeMapSiteLink(parseGeoUri(uri));
const geoCoords = parseGeoUri(uri);
return geoCoords ? makeMapSiteLink(geoCoords) : null;
}
} else {
const geoUri = content["geo_uri"];
if (geoUri) {
return makeMapSiteLink(parseGeoUri(geoUri));
const geoCoords = parseGeoUri(geoUri);
return geoCoords ? makeMapSiteLink(geoCoords) : null;
}
}
return null;

View file

@ -28,16 +28,23 @@ export const parseGeoUri = (uri: string): GeolocationCoordinates | undefined =>
if (!m) return;
const parts = m[1].split(";");
const coords = parts[0].split(",");
let uncertainty: number | null;
let uncertainty: number | null | undefined = undefined;
for (const param of parts.slice(1)) {
const m = param.match(/u=(.*)/);
if (m) uncertainty = parse(m[1]);
}
const latitude = parse(coords[0]);
const longitude = parse(coords[1]);
if (latitude === null || longitude === null) {
return;
}
return {
latitude: parse(coords[0]),
longitude: parse(coords[1]),
latitude: latitude!,
longitude: longitude!,
altitude: parse(coords[2]),
accuracy: uncertainty,
accuracy: uncertainty!,
altitudeAccuracy: null,
heading: null,
speed: null,

View file

@ -120,7 +120,7 @@ export const reorderLexicographically = (
// verify the right move would be sufficient
if (
rightBoundIdx === newOrder.length - 1 &&
(newOrder[rightBoundIdx] ? stringToBase(newOrder[rightBoundIdx].order) : BigInt(Number.MAX_VALUE)) -
(newOrder[rightBoundIdx]?.order ? stringToBase(newOrder[rightBoundIdx].order!) : BigInt(Number.MAX_VALUE)) -
prevBase <=
rightBoundIdx - toIndex
) {

View file

@ -65,6 +65,9 @@ export const lookupThreePids = async (
if (threePids.length === 0) return [];
const token = await client.identityServer.getAccessToken();
if (!token) return [];
const lookedUp = await client.bulkLookupThreePids(
threePids.map((t) => [t.isEmail ? "email" : "msisdn", t.userId]),
token,