Unverified Commit a5fa1a0d authored by Anurag Hazra's avatar Anurag Hazra Committed by GitHub
Browse files

Merge pull request #117 from anuraghazra/custom-cache

feat: added ability to set custom cache
parents 9fb28c91 fdf445d7
Loading
Loading
Loading
Loading
+15 −2
Original line number Diff line number Diff line
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 || "[]"),
+28 −2
Original line number Diff line number Diff line
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,
+1 −1
Original line number Diff line number Diff line
@@ -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}
+12 −0
Original line number Diff line number Diff line
@@ -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,
};
+78 −29
Original line number Diff line number Diff line
@@ -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}`],
      ]);
    }
  });
});