Add apiVersion to config and httpd request

We have a new apiVersion value in the config json that needs to be
changed for each breaking change on httpd so we can simulate a new
resource to the cache and get it cleared.

Also improves the ResponseParseError giving the user more information
on, if the node or the web client is outdated
This commit is contained in:
Sebastian Martinez
2024-05-03 08:34:08 +02:00
parent f5a28960bd
commit 2fc03f3312
10 changed files with 48 additions and 8 deletions
+1
View File
@@ -103,6 +103,7 @@ const nodeInfoSchema = object({
message: string(),
service: string(),
version: string(),
apiVersion: string(),
nid: string(),
path: string(),
links: array(
+32 -2
View File
@@ -3,6 +3,9 @@
import type { ZodIssue, ZodType, TypeOf } from "zod";
import { config } from "@app/lib/config";
import { compare } from "compare-versions";
export interface BaseUrl {
hostname: string;
port: number;
@@ -40,16 +43,41 @@ export class ResponseError extends Error {
export class ResponseParseError extends Error {
public method: string;
public body: unknown;
public description: string;
public apiVersion: string | undefined;
public zodIssues: ZodIssue[];
public path?: string;
public constructor(
method: string,
body: unknown,
apiVersion: string | undefined,
zodIssues: ZodIssue[],
path?: string,
) {
super("Failed to parse response body");
let description: string;
if (
apiVersion === undefined ||
compare(apiVersion, config.nodes.apiVersion, "<")
) {
description = `The node you are fetching from seems to be outdated, make sure the httpd API version is at least ${config.nodes.apiVersion} currently ${apiVersion ?? "unknown"}.`;
} else if (
config.nodes.apiVersion === undefined ||
compare(apiVersion, config.nodes.apiVersion, ">")
) {
description = `The web client you are using is outdated, make sure it supports at least ${apiVersion} to interact with this node currently ${config.nodes.apiVersion ?? "unknown"}.`;
} else {
description =
"This is usually due to a version mismatch between the seed and the web interface.";
}
this.apiVersion = apiVersion;
this.description =
"The response received from the seed does not match the expected schema.<br/>".concat(
description,
);
this.method = method;
this.path = path;
this.body = body;
@@ -93,8 +121,7 @@ export class Fetcher {
): Promise<TypeOf<T>> {
const response = await this.fetch({
...params,
// eslint-disable-next-line @typescript-eslint/naming-convention
query: { ...params.query, bust_cache: "1" },
query: { ...params.query, v: config.nodes.apiVersion },
});
if (!response.ok) {
@@ -112,9 +139,12 @@ export class Fetcher {
if (result.success) {
return result.data;
} else {
const response = await this.fetch({ method: "GET" });
const info = await response.json();
throw new ResponseParseError(
params.method,
responseBody,
info.apiVersion,
result.error.errors,
params.path,
);
+6
View File
@@ -15,6 +15,7 @@
"baconjs": "^3.0.17",
"bs58": "^5.0.0",
"buffer": "^6.0.3",
"compare-versions": "^6.1.0",
"dompurify": "^3.0.11",
"hast-util-to-dom": "^4.0.0",
"hast-util-to-html": "^9.0.0",
@@ -1880,6 +1881,11 @@
"node": ">= 12"
}
},
"node_modules/compare-versions": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.0.tgz",
"integrity": "sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg=="
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+1
View File
@@ -53,6 +53,7 @@
"baconjs": "^3.0.17",
"bs58": "^5.0.0",
"buffer": "^6.0.3",
"compare-versions": "^6.1.0",
"dompurify": "^3.0.11",
"hast-util-to-dom": "^4.0.0",
"hast-util-to-html": "^9.0.0",
+1
View File
@@ -1,6 +1,7 @@
{
"nodes": {
"fallbackPublicExplorer": "https://app.radicle.xyz/nodes/$host/$rid$path",
"apiVersion": "0.1.0",
"defaultHttpdPort": 443,
"defaultLocalHttpdPort": 8080,
"defaultHttpdHostname": "seed.radicle.garden",
+2
View File
@@ -4,6 +4,7 @@ import configJson from "@app/config.json";
export interface Config {
nodes: {
apiVersion: string;
fallbackPublicExplorer: string;
defaultHttpdPort: number;
defaultHttpdHostname: string;
@@ -21,6 +22,7 @@ function getConfig(): Config {
return {
nodes: {
fallbackPublicExplorer: "https://app.radicle.xyz/nodes/$host/$rid$path",
apiVersion: "0.1.0",
defaultHttpdHostname: "127.0.0.1",
defaultHttpdPort: 8081,
defaultLocalHttpdPort: 8081,
+1 -2
View File
@@ -12,8 +12,7 @@ export function handleError(
return {
error,
title: "Could not parse the request",
description:
"The response received from the seed does not match the expected schema, this is usually due to a version mismatch between the seed and the web interface.",
description: error.description,
};
} else if (error instanceof ResponseError) {
return {
+1 -2
View File
@@ -11,8 +11,7 @@ export function handleError(
params: {
error,
title: "Could not parse the request",
description:
"The response received from the seed does not match the expected schema, this is usually due to a version mismatch between the seed and the web interface.",
description: error.description,
},
};
} else if (error instanceof ResponseError) {
+1 -2
View File
@@ -43,8 +43,7 @@ export function handleError(
params: {
error,
title: "Could not parse the request",
description:
"The response received from the seed does not match the expected schema, this is usually due to a version mismatch between the seed and the web interface.",
description: error.description,
},
};
} else if (
+2
View File
@@ -77,6 +77,7 @@ export const test = base.extend<{
nodes: {
fallbackPublicExplorer:
"https://app.radicle.xyz/nodes/$host/$rid$path",
apiVersion: "0.1.0",
defaultHttpdPort: 8081,
defaultHttpdHostname: "127.0.0.1",
defaultLocalHttpdPort: 8081,
@@ -214,6 +215,7 @@ export function appConfigWithFixture(defaultLocalHttpdPort = 8081) {
window.APP_CONFIG = {
nodes: {
fallbackPublicExplorer: "https://app.radicle.xyz/nodes/$host/$rid$path",
apiVersion: "0.1.0",
defaultHttpdPort: 8081,
defaultHttpdHostname: "127.0.0.1",
defaultLocalHttpdPort,