mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
refactor: Replace linting configurations
The previous package was outdated, preventing us from updating TS. This one also lints YAML and JSON, and applies many more rules to the test files, explaining all the changes in this PR.
This commit is contained in:
@@ -7,6 +7,7 @@ import type { ModesExtractor } from '../authorization/permissions/ModesExtractor
|
||||
import type { AccessMap } from '../authorization/permissions/Permissions';
|
||||
import type { ResponseDescription } from '../http/output/response/ResponseDescription';
|
||||
import { getLoggerFor } from '../logging/LogUtil';
|
||||
import { createErrorMessage } from '../util/errors/ErrorUtil';
|
||||
import { HttpError } from '../util/errors/HttpError';
|
||||
import { SOLID_META } from '../util/Vocabularies';
|
||||
import type { OperationHttpHandlerInput } from './OperationHttpHandler';
|
||||
@@ -71,18 +72,20 @@ export class AuthorizingHttpHandler extends OperationHttpHandler {
|
||||
|
||||
const requestedModes = await this.modesExtractor.handleSafe(operation);
|
||||
this.logger.verbose(`Retrieved required modes: ${
|
||||
[ ...requestedModes.entrySets() ].map(([ id, set ]): string => `{ ${id.path}: ${[ ...set ]} }`)
|
||||
[ ...requestedModes.entrySets() ]
|
||||
.map(([ id, set ]): string => `{ ${id.path}: ${[ ...set ].join(',')} }`).join(',')
|
||||
}`);
|
||||
|
||||
const availablePermissions = await this.permissionReader.handleSafe({ credentials, requestedModes });
|
||||
this.logger.verbose(`Available permissions are ${
|
||||
[ ...availablePermissions.entries() ].map(([ id, map ]): string => `{ ${id.path}: ${JSON.stringify(map)} }`)
|
||||
[ ...availablePermissions.entries() ]
|
||||
.map(([ id, map ]): string => `{ ${id.path}: ${JSON.stringify(map)} }`).join(',')
|
||||
}`);
|
||||
|
||||
try {
|
||||
await this.authorizer.handleSafe({ credentials, requestedModes, availablePermissions });
|
||||
} catch (error: unknown) {
|
||||
this.logger.verbose(`Authorization failed: ${(error as any).message}`);
|
||||
this.logger.verbose(`Authorization failed: ${createErrorMessage(error)}`);
|
||||
if (HttpError.isInstance(error)) {
|
||||
this.addAccessModesToError(error, requestedModes);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Server, IncomingMessage, ServerResponse } from 'http';
|
||||
import type { IncomingMessage, Server, ServerResponse } from 'http';
|
||||
import { getLoggerFor } from '../logging/LogUtil';
|
||||
import { isError } from '../util/errors/ErrorUtil';
|
||||
import { guardStream } from '../util/GuardedStream';
|
||||
@@ -30,6 +30,7 @@ export class HandlerServerConfigurator extends ServerConfigurator {
|
||||
|
||||
public async handle(server: Server): Promise<void> {
|
||||
server.on('request',
|
||||
// eslint-disable-next-line ts/no-misused-promises
|
||||
async(request: IncomingMessage, response: ServerResponse): Promise<void> => {
|
||||
try {
|
||||
this.logger.info(`Received ${request.method} request for ${request.url}`);
|
||||
@@ -58,6 +59,7 @@ export class HandlerServerConfigurator extends ServerConfigurator {
|
||||
*/
|
||||
private createErrorMessage(error: unknown): string {
|
||||
if (!isError(error)) {
|
||||
// eslint-disable-next-line ts/restrict-template-expressions
|
||||
return `Unknown error: ${error}.\n`;
|
||||
}
|
||||
if (this.showStackTrace && error.stack) {
|
||||
|
||||
@@ -9,6 +9,7 @@ export type HttpRequest = Guarded<IncomingMessage>;
|
||||
/**
|
||||
* Checks if the given stream is an HttpRequest.
|
||||
*/
|
||||
export function isHttpRequest(stream: any): stream is HttpRequest {
|
||||
return typeof stream.socket === 'object' && typeof stream.url === 'string' && typeof stream.method === 'string';
|
||||
export function isHttpRequest(stream: unknown): stream is HttpRequest {
|
||||
const req = stream as HttpRequest;
|
||||
return typeof req.socket === 'object' && typeof req.url === 'string' && typeof req.method === 'string';
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ export class WebSocketServerConfigurator extends ServerConfigurator {
|
||||
// Create WebSocket server
|
||||
const webSocketServer = new WebSocketServer({ noServer: true });
|
||||
server.on('upgrade', (upgradeRequest: IncomingMessage, socket: Socket, head: Buffer): void => {
|
||||
// eslint-disable-next-line ts/no-misused-promises
|
||||
webSocketServer.handleUpgrade(upgradeRequest, socket, head, async(webSocket: WebSocket): Promise<void> => {
|
||||
try {
|
||||
await this.handler.handleSafe({ upgradeRequest: guardStream(upgradeRequest), webSocket });
|
||||
|
||||
@@ -4,7 +4,8 @@ import type { HttpHandlerInput } from '../HttpHandler';
|
||||
import { HttpHandler } from '../HttpHandler';
|
||||
|
||||
const defaultOptions: CorsOptions = {
|
||||
origin: (origin: any, callback: any): void => callback(null, origin ?? '*'),
|
||||
origin: (origin: string | undefined, callback: (err: Error | null, origin?: string) => void): void =>
|
||||
callback(null, origin ?? '*'),
|
||||
};
|
||||
|
||||
// Components.js does not support the full CorsOptions yet
|
||||
@@ -47,7 +48,7 @@ export class CorsHandler extends HttpHandler {
|
||||
|
||||
public async handle(input: HttpHandlerInput): Promise<void> {
|
||||
return new Promise((resolve): void => {
|
||||
this.corsHandler(input.request as any, input.response as any, (): void => resolve());
|
||||
this.corsHandler(input.request, input.response, (): void => resolve());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { APPLICATION_OCTET_STREAM } from '../../util/ContentTypes';
|
||||
import { InternalServerError } from '../../util/errors/InternalServerError';
|
||||
import { NotFoundHttpError } from '../../util/errors/NotFoundHttpError';
|
||||
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
|
||||
import type { SystemError } from '../../util/errors/SystemError';
|
||||
import { ensureTrailingSlash, joinFilePath, resolveAssetPath, trimLeadingSlashes } from '../../util/PathUtil';
|
||||
import { pipeSafely } from '../../util/StreamUtil';
|
||||
import type { HttpHandlerInput } from '../HttpHandler';
|
||||
@@ -40,7 +41,8 @@ export class StaticAssetHandler extends HttpHandler {
|
||||
* Creates a handler for the provided static resources.
|
||||
* @param assets - A list of {@link StaticAssetEntry}.
|
||||
* @param baseUrl - The base URL of the server.
|
||||
* @param options - Cache expiration time in seconds.
|
||||
* @param options - Specific options.
|
||||
* @param options.expires - Cache expiration time in seconds.
|
||||
*/
|
||||
public constructor(assets: StaticAssetEntry[], baseUrl: string, options: { expires?: number } = {}) {
|
||||
super();
|
||||
@@ -118,6 +120,7 @@ export class StaticAssetHandler extends HttpHandler {
|
||||
asset.once('readable', (): void => {
|
||||
const contentType = mime.lookup(filePath) || APPLICATION_OCTET_STREAM;
|
||||
response.writeHead(200, {
|
||||
// eslint-disable-next-line ts/naming-convention
|
||||
'content-type': contentType,
|
||||
...this.getCacheHeaders(),
|
||||
});
|
||||
@@ -135,7 +138,7 @@ export class StaticAssetHandler extends HttpHandler {
|
||||
|
||||
// Pass the error when something goes wrong
|
||||
asset.once('error', (error): void => {
|
||||
const { code } = error as any;
|
||||
const { code } = error as SystemError;
|
||||
// When the file if not found or a folder, signal a 404
|
||||
if (code === 'ENOENT' || code === 'EISDIR') {
|
||||
this.logger.debug(`Static asset ${filePath} not found`);
|
||||
@@ -153,10 +156,11 @@ export class StaticAssetHandler extends HttpHandler {
|
||||
|
||||
private getCacheHeaders(): Record<string, string> {
|
||||
return this.expires <= 0 ?
|
||||
{} :
|
||||
{
|
||||
'cache-control': `max-age=${this.expires}`,
|
||||
expires: new Date(Date.now() + (this.expires * 1000)).toUTCString(),
|
||||
};
|
||||
{} :
|
||||
{
|
||||
// eslint-disable-next-line ts/naming-convention
|
||||
'cache-control': `max-age=${this.expires}`,
|
||||
expires: new Date(Date.now() + (this.expires * 1000)).toUTCString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,5 +18,5 @@ export type ActivityEmitter =
|
||||
/**
|
||||
* A class implementation of {@link ActivityEmitter}.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
// eslint-disable-next-line ts/naming-convention
|
||||
export const BaseActivityEmitter = createGenericEventEmitterClass<ActivityEmitter>();
|
||||
|
||||
@@ -54,7 +54,9 @@ const CONTEXT_SHACL = 'https://w3c.github.io/shacl/shacl-jsonld-context/shacl.co
|
||||
* The SHACL shape for the minimum requirements on a notification channel subscription request.
|
||||
*/
|
||||
export const DEFAULT_SUBSCRIPTION_SHACL = {
|
||||
// eslint-disable-next-line ts/naming-convention
|
||||
'@context': [ CONTEXT_SHACL ],
|
||||
// eslint-disable-next-line ts/naming-convention
|
||||
'@type': 'sh:NodeShape',
|
||||
// Use the topic predicate to find the focus node
|
||||
targetSubjectsOf: NOTIFY.topic,
|
||||
@@ -109,6 +111,7 @@ export abstract class BaseChannelType implements NotificationChannelType {
|
||||
property: [
|
||||
...DEFAULT_SUBSCRIPTION_SHACL.property,
|
||||
// Add type check
|
||||
// eslint-disable-next-line ts/naming-convention
|
||||
{ path: RDF.type, hasValue: { '@id': type.value }},
|
||||
...additionalShaclProperties,
|
||||
],
|
||||
@@ -117,6 +120,7 @@ export abstract class BaseChannelType implements NotificationChannelType {
|
||||
|
||||
public getDescription(): SubscriptionService {
|
||||
return {
|
||||
// eslint-disable-next-line ts/naming-convention
|
||||
'@context': [ CONTEXT_NOTIFICATION ],
|
||||
id: this.path,
|
||||
// At the time of writing, there is no base value for URIs in the notification context,
|
||||
@@ -139,7 +143,7 @@ export abstract class BaseChannelType implements NotificationChannelType {
|
||||
* Initiates the channel by first calling {@link validateSubscription} followed by {@link quadsToChannel}.
|
||||
* Subclasses can override either function safely to impact the result of the function.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
public async initChannel(data: Store, credentials: Credentials): Promise<NotificationChannel> {
|
||||
const subject = await this.validateSubscription(data);
|
||||
return this.quadsToChannel(data, subject);
|
||||
@@ -255,6 +259,7 @@ export abstract class BaseChannelType implements NotificationChannelType {
|
||||
*/
|
||||
public async toJsonLd(channel: NotificationChannel): Promise<Record<string, unknown>> {
|
||||
const result: Record<string, unknown> = {
|
||||
// eslint-disable-next-line ts/naming-convention
|
||||
'@context': [
|
||||
CONTEXT_NOTIFICATION,
|
||||
],
|
||||
@@ -282,7 +287,7 @@ export abstract class BaseChannelType implements NotificationChannelType {
|
||||
return new IdentifierSetMultiMap<AccessMode>([[{ path: channel.topic }, AccessMode.read ]]);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
public async completeChannel(channel: NotificationChannel): Promise<void> {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ export const CONTEXT_NOTIFICATION = 'https://www.w3.org/ns/solid/notification/v1
|
||||
* as defined in https://solidproject.org/TR/2022/notifications-protocol-20221231#data-model.
|
||||
*/
|
||||
export interface Notification {
|
||||
// eslint-disable-next-line ts/naming-convention
|
||||
'@context': [
|
||||
typeof CONTEXT_ACTIVITYSTREAMS,
|
||||
typeof CONTEXT_NOTIFICATION,
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { NotificationChannel } from './NotificationChannel';
|
||||
* https://solidproject.org/TR/2022/notifications-protocol-20221231#subscription-service-data-model
|
||||
*/
|
||||
export interface SubscriptionService {
|
||||
// eslint-disable-next-line ts/naming-convention
|
||||
'@context': [ typeof CONTEXT_NOTIFICATION ];
|
||||
id: string;
|
||||
channelType: string;
|
||||
|
||||
@@ -8,6 +8,7 @@ import { APPLICATION_LD_JSON, INTERNAL_QUADS } from '../../util/ContentTypes';
|
||||
import { NOTIFY } from '../../util/Vocabularies';
|
||||
import { StorageDescriber } from '../description/StorageDescriber';
|
||||
import type { NotificationChannelType } from './NotificationChannelType';
|
||||
|
||||
const { namedNode, quad } = DataFactory;
|
||||
|
||||
/**
|
||||
|
||||
@@ -123,7 +123,7 @@ export class NotificationSubscriber extends OperationHttpHandler {
|
||||
|
||||
// Complete the channel once the response has been sent out
|
||||
endOfStream(response.data)
|
||||
.then((): Promise<void> => this.channelType.completeChannel(channel))
|
||||
.then(async(): Promise<void> => this.channelType.completeChannel(channel))
|
||||
.catch((error): void => {
|
||||
this.logger.error(`There was an issue completing notification channel ${channel.id}: ${
|
||||
createErrorMessage(error)}`);
|
||||
@@ -134,10 +134,10 @@ export class NotificationSubscriber extends OperationHttpHandler {
|
||||
|
||||
private async authorize(credentials: Credentials, channel: NotificationChannel): Promise<void> {
|
||||
const requestedModes = await this.channelType.extractModes(channel);
|
||||
this.logger.debug(`Retrieved required modes: ${[ ...requestedModes.entrySets() ]}`);
|
||||
this.logger.debug(`Retrieved required modes: ${[ ...requestedModes.entrySets() ].join(',')}`);
|
||||
|
||||
const availablePermissions = await this.permissionReader.handleSafe({ credentials, requestedModes });
|
||||
this.logger.debug(`Available permissions are ${[ ...availablePermissions.entries() ]}`);
|
||||
this.logger.debug(`Available permissions are ${[ ...availablePermissions.entries() ].join(',')}`);
|
||||
|
||||
await this.authorizer.handleSafe({ credentials, requestedModes, availablePermissions });
|
||||
this.logger.debug(`Authorization succeeded, creating notification channel`);
|
||||
|
||||
@@ -88,6 +88,7 @@ export class WebhookEmitter extends NotificationEmitter {
|
||||
const response = await fetch(webhookChannel.sendTo, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
// eslint-disable-next-line ts/naming-convention
|
||||
'content-type': representation.metadata.contentType!,
|
||||
authorization: `DPoP ${dpopToken}`,
|
||||
dpop: dpopProof,
|
||||
|
||||
@@ -33,6 +33,7 @@ export class ActivityNotificationGenerator extends NotificationGenerator {
|
||||
const state = this.eTagHandler.getETag(representation.metadata);
|
||||
|
||||
return {
|
||||
// eslint-disable-next-line ts/naming-convention
|
||||
'@context': [
|
||||
CONTEXT_ACTIVITYSTREAMS,
|
||||
CONTEXT_NOTIFICATION,
|
||||
|
||||
@@ -43,6 +43,7 @@ export class AddRemoveNotificationGenerator extends NotificationGenerator {
|
||||
}
|
||||
|
||||
return {
|
||||
// eslint-disable-next-line ts/naming-convention
|
||||
'@context': [
|
||||
CONTEXT_ACTIVITYSTREAMS,
|
||||
CONTEXT_NOTIFICATION,
|
||||
|
||||
@@ -19,6 +19,7 @@ export class DeleteNotificationGenerator extends NotificationGenerator {
|
||||
|
||||
public async handle({ topic }: NotificationHandlerInput): Promise<Notification> {
|
||||
return {
|
||||
// eslint-disable-next-line ts/naming-convention
|
||||
'@context': [
|
||||
CONTEXT_ACTIVITYSTREAMS,
|
||||
CONTEXT_NOTIFICATION,
|
||||
|
||||
@@ -47,7 +47,7 @@ export abstract class BaseRouterHandler<T extends AsyncHandler<any, any>>
|
||||
protected constructor(args: BaseRouterHandlerArgs<T>) {
|
||||
super();
|
||||
if (typeof args.allowedPathNames !== 'undefined' && typeof args.baseUrl !== 'string') {
|
||||
throw new Error('A value for allowedPathNames requires baseUrl to be defined.');
|
||||
throw new TypeError('A value for allowedPathNames requires baseUrl to be defined.');
|
||||
}
|
||||
// Trimming trailing slash so regexes can start with `/`
|
||||
this.baseUrlLength = trimTrailingSlashes(args.baseUrl ?? '').length;
|
||||
@@ -70,6 +70,6 @@ export abstract class BaseRouterHandler<T extends AsyncHandler<any, any>>
|
||||
}
|
||||
|
||||
public async handle(input: AsyncHandlerInput<T>): Promise<AsyncHandlerOutput<T>> {
|
||||
return this.handler.handle(input);
|
||||
return this.handler.handle(input) as Promise<AsyncHandlerOutput<T>>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { OperationHttpHandlerInput, OperationHttpHandler } from '../OperationHttpHandler';
|
||||
import type { OperationHttpHandler, OperationHttpHandlerInput } from '../OperationHttpHandler';
|
||||
import type { BaseRouterHandlerArgs } from './BaseRouterHandler';
|
||||
import { BaseRouterHandler } from './BaseRouterHandler';
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import type { HttpHandlerInput } from '../HttpHandler';
|
||||
import { HttpHandler } from '../HttpHandler';
|
||||
import type { HttpRequest } from '../HttpRequest';
|
||||
|
||||
/* eslint-disable ts/naming-convention */
|
||||
const redirectErrorFactories: Record<301 | 302 | 303 | 307 | 308, (location: string) => RedirectHttpError> = {
|
||||
301: (location: string): RedirectHttpError => new MovedPermanentlyHttpError(location),
|
||||
302: (location: string): RedirectHttpError => new FoundHttpError(location),
|
||||
@@ -21,6 +22,7 @@ const redirectErrorFactories: Record<301 | 302 | 303 | 307 | 308, (location: str
|
||||
307: (location: string): RedirectHttpError => new TemporaryRedirectHttpError(location),
|
||||
308: (location: string): RedirectHttpError => new PermanentRedirectHttpError(location),
|
||||
};
|
||||
/* eslint-enable ts/naming-convention */
|
||||
|
||||
/**
|
||||
* Handler that redirects paths matching given patterns
|
||||
@@ -36,6 +38,7 @@ export class RedirectingHttpHandler extends HttpHandler {
|
||||
/**
|
||||
* Creates a handler for the provided redirects.
|
||||
* @param redirects - A mapping between URL patterns.
|
||||
* @param baseUrl - Base URL of the server.
|
||||
* @param targetExtractor - To extract the target from the request.
|
||||
* @param responseWriter - To write the redirect to the response.
|
||||
* @param statusCode - Desired 30x redirection code (defaults to 308).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { TargetExtractor } from '../../http/input/identifier/TargetExtractor';
|
||||
import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
|
||||
import type { HttpHandlerInput, HttpHandler } from '../HttpHandler';
|
||||
import type { HttpHandler, HttpHandlerInput } from '../HttpHandler';
|
||||
import { BaseRouterHandler } from './BaseRouterHandler';
|
||||
import type { BaseRouterHandlerArgs } from './BaseRouterHandler';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user