Merge pull request #117 from anuraghazra/custom-cache

feat: added ability to set custom cache
This commit is contained in:
Anurag Hazra 2020-07-20 21:56:16 +05:30 committed by GitHub
commit a5fa1a0d1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 134 additions and 34 deletions

View File

@ -1,5 +1,10 @@
require("dotenv").config();
const { renderError, parseBoolean } = require("../src/utils");
const {
renderError,
parseBoolean,
clampValue,
CONSTANTS,
} = require("../src/utils");
const fetchStats = require("../src/fetchStats");
const renderStatsCard = require("../src/renderStatsCard");
@ -17,10 +22,10 @@ module.exports = async (req, res) => {
text_color,
bg_color,
theme,
cache_seconds,
} = req.query;
let stats;
res.setHeader("Cache-Control", "public, max-age=1800");
res.setHeader("Content-Type", "image/svg+xml");
try {
@ -29,6 +34,14 @@ module.exports = async (req, res) => {
return res.send(renderError(err.message));
}
const cacheSeconds = clampValue(
parseInt(cache_seconds || CONSTANTS.THIRTY_MINUTES, 10),
CONSTANTS.THIRTY_MINUTES,
CONSTANTS.ONE_DAY
);
res.setHeader("Cache-Control", `public, max-age=${cacheSeconds}`);
res.send(
renderStatsCard(stats, {
hide: JSON.parse(hide || "[]"),

View File

@ -1,5 +1,10 @@
require("dotenv").config();
const { renderError, parseBoolean } = require("../src/utils");
const {
renderError,
parseBoolean,
clampValue,
CONSTANTS,
} = require("../src/utils");
const fetchRepo = require("../src/fetchRepo");
const renderRepoCard = require("../src/renderRepoCard");
@ -13,11 +18,11 @@ module.exports = async (req, res) => {
bg_color,
theme,
show_owner,
cache_seconds,
} = req.query;
let repoData;
res.setHeader("Cache-Control", "public, max-age=1800");
res.setHeader("Content-Type", "image/svg+xml");
try {
@ -27,6 +32,27 @@ module.exports = async (req, res) => {
return res.send(renderError(err.message));
}
let cacheSeconds = clampValue(
parseInt(cache_seconds || CONSTANTS.THIRTY_MINUTES, 10),
CONSTANTS.THIRTY_MINUTES,
CONSTANTS.ONE_DAY
);
/*
if star count & fork count is over 1k then we are kFormating the text
and if both are zero we are not showing the stats
so we can just make the cache longer, since there is no need to frequent updates
*/
const stars = repoData.stargazers.totalCount;
const forks = repoData.forkCount;
const isBothOver1K = stars > 1000 && forks > 1000;
const isBothUnder1 = stars < 1 && forks < 1;
if (!cache_seconds && (isBothOver1K || isBothUnder1)) {
cacheSeconds = CONSTANTS.TWO_HOURS;
}
res.setHeader("Cache-Control", `public, max-age=${cacheSeconds}`);
res.send(
renderRepoCard(repoData, {
title_color,

View File

@ -75,7 +75,7 @@ const renderRepoCard = (repo, options = {}) => {
`;
const svgForks =
totalForks > 0 &&
forkCount > 0 &&
`
<svg class="icon" y="-12" viewBox="0 0 16 16" version="1.1" width="16" height="16">
${icons.fork}

View File

@ -44,6 +44,10 @@ function parseBoolean(value) {
}
}
function clampValue(number, min, max) {
return Math.max(min, Math.min(number, max));
}
function fallbackColor(color, fallbackColor) {
return (isValidHexColor(color) && `#${color}`) || fallbackColor;
}
@ -112,6 +116,12 @@ function getCardColors({
return { titleColor, iconColor, textColor, bgColor };
}
const CONSTANTS = {
THIRTY_MINUTES: 1800,
TWO_HOURS: 7200,
ONE_DAY: 86400,
};
module.exports = {
renderError,
kFormatter,
@ -122,4 +132,6 @@ module.exports = {
fallbackColor,
FlexLayout,
getCardColors,
clampValue,
CONSTANTS,
};

View File

@ -3,7 +3,7 @@ const axios = require("axios");
const MockAdapter = require("axios-mock-adapter");
const api = require("../api/index");
const renderStatsCard = require("../src/renderStatsCard");
const { renderError } = require("../src/utils");
const { renderError, CONSTANTS } = require("../src/utils");
const calculateRank = require("../src/calculateRank");
const stats = {
@ -55,15 +55,11 @@ const error = {
const mock = new MockAdapter(axios);
afterEach(() => {
mock.reset();
});
describe("Test /api/", () => {
it("should test the request", async () => {
const faker = (query, data) => {
const req = {
query: {
username: "anuraghazra",
...query,
},
};
const res = {
@ -72,6 +68,17 @@ describe("Test /api/", () => {
};
mock.onPost("https://api.github.com/graphql").reply(200, data);
return { req, res };
};
afterEach(() => {
mock.reset();
});
describe("Test /api/", () => {
it("should test the request", async () => {
const { req, res } = faker({}, data);
await api(req, res);
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
@ -79,16 +86,7 @@ describe("Test /api/", () => {
});
it("should render error card on error", async () => {
const req = {
query: {
username: "anuraghazra",
},
};
const res = {
setHeader: jest.fn(),
send: jest.fn(),
};
mock.onPost("https://api.github.com/graphql").reply(200, error);
const { req, res } = faker({}, error);
await api(req, res);
@ -97,8 +95,8 @@ describe("Test /api/", () => {
});
it("should get the query options", async () => {
const req = {
query: {
const { req, res } = faker(
{
username: "anuraghazra",
hide: `["issues","prs","contribs"]`,
show_icons: true,
@ -109,12 +107,8 @@ describe("Test /api/", () => {
text_color: "fff",
bg_color: "fff",
},
};
const res = {
setHeader: jest.fn(),
send: jest.fn(),
};
mock.onPost("https://api.github.com/graphql").reply(200, data);
data
);
await api(req, res);
@ -132,4 +126,59 @@ describe("Test /api/", () => {
})
);
});
it("should have proper cache", async () => {
const { req, res } = faker({}, data);
mock.onPost("https://api.github.com/graphql").reply(200, data);
await api(req, res);
expect(res.setHeader.mock.calls).toEqual([
["Content-Type", "image/svg+xml"],
["Cache-Control", `public, max-age=${CONSTANTS.THIRTY_MINUTES}`],
]);
});
it("should set proper cache", async () => {
const { req, res } = faker({ cache_seconds: 2000 }, data);
await api(req, res);
expect(res.setHeader.mock.calls).toEqual([
["Content-Type", "image/svg+xml"],
["Cache-Control", `public, max-age=${2000}`],
]);
});
it("should set proper cache with clamped values", async () => {
{
let { req, res } = faker({ cache_seconds: 200000 }, data);
await api(req, res);
expect(res.setHeader.mock.calls).toEqual([
["Content-Type", "image/svg+xml"],
["Cache-Control", `public, max-age=${CONSTANTS.ONE_DAY}`],
]);
}
// note i'm using block scoped vars
{
let { req, res } = faker({ cache_seconds: 0 }, data);
await api(req, res);
expect(res.setHeader.mock.calls).toEqual([
["Content-Type", "image/svg+xml"],
["Cache-Control", `public, max-age=${CONSTANTS.THIRTY_MINUTES}`],
]);
}
{
let { req, res } = faker({ cache_seconds: -10000 }, data);
await api(req, res);
expect(res.setHeader.mock.calls).toEqual([
["Content-Type", "image/svg+xml"],
["Cache-Control", `public, max-age=${CONSTANTS.THIRTY_MINUTES}`],
]);
}
});
});