Commit 2fa14126 authored by Robert Knight's avatar Robert Knight

Require types for all JSON responses

Make `fetchJSON` return `Promise<unknown>` rather than `Promise<any>`
and add appropriate types and casts for the remaining API / OAuth
responses that didn't have them.
parent 3cc78b11
......@@ -17,7 +17,10 @@ function getJSON(url) {
}
/**
* @typedef {import('../../types/api').IndexResponse} IndexResponse
* @typedef {import('../../types/api').LinksResponse} LinksResponse
* @typedef {import('../../types/api').RouteMap} RouteMap
* @typedef {import('../../types/api').RouteMetadata} RouteMetadata
* @typedef {import('../../types/config').SidebarSettings} SidebarSettings
*/
......@@ -30,12 +33,12 @@ export class APIRoutesService {
* @param {SidebarSettings} settings
*/
constructor(settings) {
this._apiUrl = settings.apiUrl;
this._apiURL = settings.apiUrl;
/** @type {Promise<RouteMap>|null} */
this._routeCache = null;
/** @type {Promise<Record<string,string>>|null} */
/** @type {Promise<LinksResponse>|null} */
this._linkCache = null;
}
......@@ -49,7 +52,10 @@ export class APIRoutesService {
*/
routes() {
if (!this._routeCache) {
this._routeCache = getJSON(this._apiUrl).then(index => index.links);
this._routeCache = getJSON(this._apiURL).then(result => {
const index = /** @type {IndexResponse} */ (result);
return index.links;
});
}
return this._routeCache;
}
......@@ -57,13 +63,15 @@ export class APIRoutesService {
/**
* Fetch and cache service page links from the API.
*
* @return {Promise<Record<string,string>>} - Map of link name to URL
* @return {Promise<LinksResponse>}
*/
links() {
if (!this._linkCache) {
this._linkCache = this.routes().then(routes =>
getJSON(/** @type {string} */ (routes.links.url))
);
this._linkCache = this.routes().then(async routes => {
const linksRoute = /** @type {RouteMetadata} */ (routes.links);
const links = await getJSON(linksRoute.url);
return /** @type {LinksResponse} */ (links);
});
}
return this._linkCache;
}
......
......@@ -41,7 +41,7 @@ export class FetchError extends Error {
*
* @param {string} url
* @param {RequestInit} [init] - Parameters for `fetch` request
* @return {Promise<any>} - Parsed JSON response or `null` if response status is 204 (No Content)
* @return {Promise<unknown>} - Parsed JSON response or `null` if response status is 204 (No Content)
* @throws {FetchError} if the request fails, returns a non-2xx status or a JSON
* response is expected but cannot be parsed
*/
......
import { generateHexString } from '../../shared/random';
import { fetchJSON } from './fetch';
/**
* OAuth access token response.
*
* See https://datatracker.ietf.org/doc/html/rfc6749#section-5.1
*
* @typedef AccessTokenResponse
* @prop {string} access_token
* @prop {number} expires_in
* @prop {string} refresh_token
*/
/**
* An object holding the details of an access token from the tokenUrl endpoint.
*
......@@ -213,15 +224,17 @@ export class OAuthClient {
/**
* Fetch an OAuth access token.
*
* See https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3
*
* @param {Record<string, string>} data - Parameters for form POST request
* @return {Promise<TokenInfo>}
*/
async _getAccessToken(data) {
// The request to `tokenEndpoint` returns an OAuth "Access Token Response".
// See https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.4
let response;
try {
response = await this._formPost(this.tokenEndpoint, data);
response = /** @type {AccessTokenResponse} */ (
await this._formPost(this.tokenEndpoint, data)
);
} catch (err) {
throw new TokenError('Failed to fetch access token', err);
}
......
......@@ -6,7 +6,7 @@
*/
/**
* An entry in the API index response (`/api`) describing an API route.
* Metadata specifying how to call an API route.
*
* @typedef RouteMetadata
* @prop {string} method - HTTP method
......@@ -15,12 +15,27 @@
*/
/**
* Structure of the `links` field of the API index response (`/api`) describing
* available API routes.
* A nested map of API route name to route metadata.
*
* @typedef {{ [key: string]: RouteMap|RouteMetadata }} RouteMap
*/
/**
* Structure of the API index response (`/api`).
*
* @typedef IndexResponse
* @prop {RouteMap} links
*/
/**
* Structure of the Hypothesis links response (`/api/links`).
*
* This is a map of link name (eg. "account.settings") to URL. The URL may
* include ":"-prefixed placeholders/variables.
*
* @typedef {Record<string, string>} LinksResponse
*/
/**
* Selector which identifies a document region using the selected text plus
* the surrounding context.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment