Skip to content

Commit

Permalink
Merge pull request #325 from lidofinance/feature/si-1390-unused-api-e…
Browse files Browse the repository at this point in the history
…ndpoints-deprecation

Unused api endpoints deprecation
  • Loading branch information
itaven authored Jun 19, 2024
2 parents c349089 + d2874e4 commit eddf917
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 12 deletions.
25 changes: 25 additions & 0 deletions consts/api.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { config } from 'config';

export const ETHPLORER_TOKEN_ENDPOINT =
'https://api.ethplorer.io/getTokenInfo/';

export const HEALTHY_RPC_SERVICES_ARE_OVER = 'Healthy RPC services are over!';

// 5rd August, Monday, middle of the working day
export const API_DEFAULT_SUNSET_TIMESTAMP = new Date(
'2024-08-05T09:00:00',
).getTime();

export const enum API_ROUTES {
ETH_APR = 'api/eth-apr',
ETH_PRICE = 'api/eth-price',
Expand All @@ -17,3 +24,21 @@ export const enum API_ROUTES {
METRICS = 'api/metrics',
REWARDS = 'api/rewards',
}

const getEthApiOrigin = (path: string) => {
const { hostname, protocol } = new URL(config.rootOrigin);
return protocol + '//' + 'eth-api.' + hostname + path;
};

export const getReplacementLink = (
apiRoute: API_ROUTES,
): string | undefined => {
switch (apiRoute) {
case API_ROUTES.ETH_APR:
return getEthApiOrigin('/v1/protocol/eth/apr/last');
case API_ROUTES.SMA_STETH_APR:
return getEthApiOrigin('/v1/protocol/steth/apr/sma');
default:
return;
}
};
11 changes: 10 additions & 1 deletion pages/api/eth-apr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ import { wrapRequest as wrapNextRequest } from '@lidofinance/next-api-wrapper';

import { API } from 'types';
import { config } from 'config';
import { API_ROUTES } from 'consts/api';
import {
API_DEFAULT_SUNSET_TIMESTAMP,
API_ROUTES,
getReplacementLink,
} from 'consts/api';
import {
getEthApr,
errorAndCacheDefaultWrappers,
responseTimeMetric,
rateLimit,
sunsetBy,
httpMethodGuard,
HttpMethod,
} from 'utilsApi';
Expand Down Expand Up @@ -36,5 +41,9 @@ export default wrapNextRequest([
httpMethodGuard([HttpMethod.GET]),
rateLimit,
responseTimeMetric(Metrics.request.apiTimings, API_ROUTES.ETH_APR),
sunsetBy({
sunsetTimestamp: API_DEFAULT_SUNSET_TIMESTAMP,
replacementLink: getReplacementLink(API_ROUTES.ETH_APR),
}),
...errorAndCacheDefaultWrappers,
])(ethApr);
10 changes: 7 additions & 3 deletions pages/api/ldo-stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { wrapRequest as wrapNextRequest } from '@lidofinance/next-api-wrapper';

import { config } from 'config';

import { API_ROUTES } from 'consts/api';
import { API_DEFAULT_SUNSET_TIMESTAMP, API_ROUTES } from 'consts/api';
import {
getLdoStats,
errorAndCacheDefaultWrappers,
responseTimeMetric,
rateLimit,
sunsetBy,
httpMethodGuard,
HttpMethod,
} from 'utilsApi';
Expand All @@ -24,7 +25,7 @@ const ldoStats: API = async (req, res) => {
const cachedLidoStats = cache.get(config.CACHE_LDO_STATS_KEY);

if (cachedLidoStats) {
res.status(200).json(cachedLidoStats);
res.json(cachedLidoStats);
} else {
const ldoStats = await getLdoStats();

Expand All @@ -34,13 +35,16 @@ const ldoStats: API = async (req, res) => {
config.CACHE_LDO_STATS_TTL,
);

res.status(200).json({ data: ldoStats });
res.json({ data: ldoStats });
}
};

export default wrapNextRequest([
httpMethodGuard([HttpMethod.GET]),
rateLimit,
responseTimeMetric(Metrics.request.apiTimings, API_ROUTES.LDO_STATS),
sunsetBy({
sunsetTimestamp: API_DEFAULT_SUNSET_TIMESTAMP,
}),
...errorAndCacheDefaultWrappers,
])(ldoStats);
10 changes: 7 additions & 3 deletions pages/api/lido-stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { Cache } from 'memory-cache';
import { wrapRequest as wrapNextRequest } from '@lidofinance/next-api-wrapper';

import { config } from 'config';
import { API_ROUTES } from 'consts/api';
import { API_DEFAULT_SUNSET_TIMESTAMP, API_ROUTES } from 'consts/api';
import {
getLidoStats,
errorAndCacheDefaultWrappers,
responseTimeMetric,
rateLimit,
sunsetBy,
httpMethodGuard,
HttpMethod,
} from 'utilsApi';
Expand All @@ -23,7 +24,7 @@ const lidoStats: API = async (req, res) => {
const cachedLidoStats = cache.get(config.CACHE_LIDO_STATS_KEY);

if (cachedLidoStats) {
res.status(200).json(cachedLidoStats);
res.json(cachedLidoStats);
} else {
const lidoStats = await getLidoStats();
cache.put(
Expand All @@ -32,13 +33,16 @@ const lidoStats: API = async (req, res) => {
config.CACHE_LIDO_STATS_TTL,
);

res.status(200).json({ data: lidoStats });
res.json({ data: lidoStats });
}
};

export default wrapNextRequest([
httpMethodGuard([HttpMethod.GET]),
rateLimit,
responseTimeMetric(Metrics.request.apiTimings, API_ROUTES.LIDO_STATS),
sunsetBy({
sunsetTimestamp: API_DEFAULT_SUNSET_TIMESTAMP,
}),
...errorAndCacheDefaultWrappers,
])(lidoStats);
10 changes: 7 additions & 3 deletions pages/api/lidostats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { Cache } from 'memory-cache';
import { wrapRequest as wrapNextRequest } from '@lidofinance/next-api-wrapper';

import { config } from 'config';
import { API_ROUTES } from 'consts/api';
import { API_DEFAULT_SUNSET_TIMESTAMP, API_ROUTES } from 'consts/api';
import {
getLidoStats,
responseTimeMetric,
errorAndCacheDefaultWrappers,
rateLimit,
sunsetBy,
httpMethodGuard,
HttpMethod,
} from 'utilsApi';
Expand All @@ -24,7 +25,7 @@ const lidoStats: API = async (req, res) => {
const cachedLidoStats = cache.get(config.CACHE_LIDO_STATS_KEY);

if (cachedLidoStats) {
res.status(200).json(cachedLidoStats);
res.json(cachedLidoStats);
} else {
const lidoStats = await getLidoStats();
cache.put(
Expand All @@ -33,13 +34,16 @@ const lidoStats: API = async (req, res) => {
config.CACHE_LIDO_STATS_TTL,
);

res.status(200).json({ data: lidoStats });
res.json({ data: lidoStats });
}
};

export default wrapNextRequest([
httpMethodGuard([HttpMethod.GET]),
rateLimit,
responseTimeMetric(Metrics.request.apiTimings, API_ROUTES.LIDOSTATS),
sunsetBy({
sunsetTimestamp: API_DEFAULT_SUNSET_TIMESTAMP,
}),
...errorAndCacheDefaultWrappers,
])(lidoStats);
11 changes: 10 additions & 1 deletion pages/api/sma-steth-apr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ import { wrapRequest as wrapNextRequest } from '@lidofinance/next-api-wrapper';

import { API } from 'types';
import { config } from 'config';
import { API_ROUTES } from 'consts/api';
import {
API_DEFAULT_SUNSET_TIMESTAMP,
API_ROUTES,
getReplacementLink,
} from 'consts/api';
import {
responseTimeMetric,
errorAndCacheDefaultWrappers,
rateLimit,
getSmaStethApr,
sunsetBy,
httpMethodGuard,
HttpMethod,
} from 'utilsApi';
Expand Down Expand Up @@ -38,5 +43,9 @@ export default wrapNextRequest([
httpMethodGuard([HttpMethod.GET]),
rateLimit,
responseTimeMetric(Metrics.request.apiTimings, API_ROUTES.SMA_STETH_APR),
sunsetBy({
sunsetTimestamp: API_DEFAULT_SUNSET_TIMESTAMP,
replacementLink: getReplacementLink(API_ROUTES.SMA_STETH_APR),
}),
...errorAndCacheDefaultWrappers,
])(smaStethApr);
5 changes: 5 additions & 0 deletions test/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { CONFIG } from './config.js';
export interface GetRequest {
uri: string;
schema: object;
isDeprecated?: boolean;
skipTestnet?: boolean;
}

Expand Down Expand Up @@ -196,6 +197,7 @@ export const GET_REQUESTS: GetRequest[] = [
},
{
uri: '/api/eth-apr',
isDeprecated: true,
schema: { type: 'string', pattern: FLOAT_REGEX },
},
{
Expand All @@ -204,10 +206,12 @@ export const GET_REQUESTS: GetRequest[] = [
},
{
uri: '/api/lidostats',
isDeprecated: true,
schema: LIDO_STATS_SCHEMA,
},
{
uri: '/api/ldo-stats',
isDeprecated: true,
schema: LIDO_STATS_SCHEMA,
},
{
Expand Down Expand Up @@ -272,6 +276,7 @@ export const GET_REQUESTS: GetRequest[] = [
},
{
uri: '/api/sma-steth-apr',
isDeprecated: true,
schema: {
type: 'string',
pattern: FLOAT_REGEX,
Expand Down
17 changes: 16 additions & 1 deletion test/smoke.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,22 @@ test.describe('Smoke GET', () => {
if (CONFIG.STAND_TYPE === 'testnet' && element.skipTestnet) return;

const resp = await request.get(element.uri);
expect(resp.status()).toBe(200);

if (element.isDeprecated) {
expect([299, 410]).toContain(resp.status());
if (resp.status() === 299) {
expect(resp.headers()).toHaveProperty('warning');
expect(resp.headers()).toHaveProperty('deprecation');
expect(resp.headers()).toHaveProperty('sunset');
// continue default check
} else {
// on 410, nothing to check
return;
}
} else {
expect(resp.status()).toBe(200);
}

const validationResult = validator.validate(
await resp.json(),
element.schema,
Expand Down
37 changes: 37 additions & 0 deletions utilsApi/nextApiWrappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,43 @@ export const nextDefaultErrorHandler =
}
};

type sunsetByArgs = {
replacementLink?: string;
sunsetTimestamp: number;
};

export const sunsetBy =
({ replacementLink, sunsetTimestamp }: sunsetByArgs): RequestWrapper =>
async (req, res, next) => {
console.warn('Request to deprecated endpoint:', req.url);
const shouldDisable = Date.now() > sunsetTimestamp;

if (shouldDisable) {
if (replacementLink) {
res.setHeader('Location', replacementLink);
// Permanent Redirect
res.status(301);
} else {
// Gone
res.status(410);
}
res.end();
} else {
const sunsetDate = new Date(sunsetTimestamp).toUTCString();
res.status(299);
res.setHeader(
'Warning',
`299 - "this resource will be sunset by ${sunsetDate}"`,
);
res.setHeader('Deprecation', 'true');
res.setHeader('Sunset', sunsetDate);
if (replacementLink) {
res.setHeader('Link', `${replacementLink}; rel="alternate"`);
}
await next?.(req, res, next);
}
};

export const defaultErrorHandler = nextDefaultErrorHandler({
serverLogger: console,
});
Expand Down

0 comments on commit eddf917

Please sign in to comment.