Skip to content

Commit

Permalink
Add a routing cache interface to the common/lib
Browse files Browse the repository at this point in the history
  • Loading branch information
Tzal3x committed Oct 8, 2024
1 parent e86a2b6 commit f9ca248
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 12 deletions.
41 changes: 31 additions & 10 deletions portal/common/lib/page_fetching.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import { getFullnodeUrl, SuiClient } from "@mysten/sui/client";
import { NETWORK } from "./constants";
import { DomainDetails, isResource } from "./types/index";
import { DomainDetails, isResource, isRoutes } from "./types/index";
import { subdomainToObjectId, HEXtoBase36 } from "./objectId_operations";
import { resolveSuiNsAddress, hardcodedSubdmains } from "./suins";
import { fetchResource } from "./resource";
Expand All @@ -17,29 +17,50 @@ import { aggregatorEndpoint } from "./aggregator";
import { toB64 } from "@mysten/bcs";
import { sha256 } from "./crypto";
import { getRoutes, matchPathToRoute } from "./routing";
import { RoutingCacheInterface } from "./routing_cache_interface";
import { Routes, Empty, isEmpty } from "./types/index";

/**
* Resolves the subdomain to an object ID, and gets the corresponding resources.
*/
export async function resolveAndFetchPage(parsedUrl: DomainDetails): Promise<Response> {
export async function resolveAndFetchPage(
parsedUrl: DomainDetails, cache?: RoutingCacheInterface
): Promise<Response> {
const rpcUrl = getFullnodeUrl(NETWORK);
const client = new SuiClient({ url: rpcUrl });
const resolveObjectResult = await resolveObjectId(parsedUrl, client);
const isObjectId = typeof resolveObjectResult == "string";
if (isObjectId) {
console.log("Object ID: ", resolveObjectResult);
console.log("Object ID: ", resolveObjectResult);
console.log("Base36 version of the object ID: ", HEXtoBase36(resolveObjectResult));
// Rerouting based on the contents of the routes object,
// constructed using the ws-resource.json.
const routes = await getRoutes(client, resolveObjectResult);
if (!routes) {
console.warn("No routes found for the object ID");
return fetchPage(client, resolveObjectResult, parsedUrl.path);
let routes: Routes | Empty | undefined;
if (cache) {
routes = await cache.get(resolveObjectResult);
if (!routes) {
// The routes object was not found in the cache, so we need to fetch it.
routes = await getRoutes(client, resolveObjectResult);
await cache.set(resolveObjectResult, routes ? routes : {});
}
if (isEmpty(routes)) {
console.warn("The routes object was already fetched, but it was empty.");
return fetchPage(client, resolveObjectResult, parsedUrl.path);
}
} else {
console.warn("No cache provided, fetching the routes object on every request.");
routes = await getRoutes(client, resolveObjectResult);
if (!routes) {
console.warn("No routes found for the object ID");
return fetchPage(client, resolveObjectResult, parsedUrl.path);
}
}
let matchingRoute: string | undefined;
matchingRoute = matchPathToRoute(parsedUrl.path, routes)
if (!matchingRoute) {
console.warn(`No matching route found for ${parsedUrl.path}`);
if (isRoutes(routes)) {
matchingRoute = matchPathToRoute(parsedUrl.path, routes)
if (!matchingRoute) {
console.warn(`No matching route found for ${parsedUrl.path}`);
}
}
return fetchPage(client, resolveObjectResult, matchingRoute ?? parsedUrl.path);
}
Expand Down
18 changes: 18 additions & 0 deletions portal/common/lib/routing_cache_interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { Routes, Empty } from "./types";

/**
* Abstract class for a cache, to be able to swap out different
* caches between different portal implementations.
*/
export interface RoutingCacheInterface {
get(key: string): Promise<Routes | Empty | undefined>;
/// A note on Empty:
/// This is used to indicate that the cache does not have a value for the key.
/// It is used to differentiate between a cache miss and a cache hit with an empty value.
/// Cache hit with empty values should not trigger a fetch from the fullnode.
set(key: string, value: Routes | Empty): Promise<void>;
delete(key: string): Promise<void>;
}
12 changes: 12 additions & 0 deletions portal/common/lib/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,15 @@ export function isRoutes(obj: any): obj is Routes {
obj.routes_list instanceof Map
);
}

/// An empty object that cannot have any properties.
export type Empty = Record<string, never>;

/**
* Checks if an object is of type Empty.
* @param obj The object to check.
* @returns True if the object is Empty, false otherwise.
*/
export function isEmpty(obj: any): obj is Empty {
return Object.keys(obj).length === 0;
}
2 changes: 0 additions & 2 deletions portal/server/next-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,3 @@
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript
// for more information.

0 comments on commit f9ca248

Please sign in to comment.