Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default {
testEnvironment: "node",
transform: {},
};
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jetsetradio-api",
"version": "1.1.5",
"version": "1.1.6",
"description": "A Data Provider relating to the JSR/JSRF universe",
"type": "module",
"main": "src/app.js",
Expand Down
10 changes: 10 additions & 0 deletions src/controllers/artistController.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Constants from "../constants/dbConstants.js";
import {Actions} from "../config/dbActions.js";
import {performDBAction} from "../config/db.js";
import LOGGER from "../utils/logger.js";
import {fetchRandom} from "./utilController.js";

const Artist = "Artist";
const Song = "Song";
Expand All @@ -21,6 +22,15 @@ export const getArtists = async (req, res) => {
}
};

export const getRandomArtist = async (req, res) => {
try {
res.send(await fetchRandom(req, Artist, "N/A"));
} catch (err) {
LOGGER.error(`Could not fetch random Artist`, err);
res.status(500).json({error: "Failed to fetch random Artist"});
}
};

export const getArtistById = async (req, res) => {
try {
const artist = await performDBAction(
Expand Down
113 changes: 113 additions & 0 deletions src/controllers/graffitiSoulController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import Constants from "../constants/dbConstants.js";
import {Actions} from "../config/dbActions.js";
import {performDBAction} from "../config/db.js";
import {sortObjects} from "../utils/utility.js";
import LOGGER from "../utils/logger.js";
import {fetchRandom} from "./utilController.js";

const GraffitiSoul = "GraffitiSoul";
const {JSR_DB, JSRF_DB} = Constants;

export const getAllGraffitiSouls = async (req, res) => {
try {
const sortByValue = req?.query?.sortBy ? req?.query?.sortBy : undefined;
const sortOrder = req?.query?.orderBy ? req?.query?.orderBy : "asc";
const jsrSouls = await fetchJSRSouls(req);
const jsrfSouls = await fetchJSRFSouls(req);
if (sortByValue) {
const Souls = [...jsrSouls, ...jsrfSouls];
return res.send(Souls.sort(sortObjects(sortByValue, sortOrder)));
}
res.send([...jsrSouls, ...jsrfSouls]);
} catch (err) {
LOGGER.error(`Could not fetch ALL GraffitiSouls`, err);
res
.status(500)
.json({message: "Failed to fetch ALL GraffitiSouls", err: err});
}
};

export const getRandomGraffitiSoul = async (req, res) => {
try {
res.send(await fetchRandom(req, GraffitiSoul));
} catch (err) {
LOGGER.error(`Could not fetch random GraffitiSoul`, err);
res.status(500).json({error: "Failed to fetch random GraffitiSoul"});
}
};

export const getJSRGraffitiSouls = async (req, res) => {
try {
res.send(await fetchJSRSouls(req));
} catch (err) {
LOGGER.error(`Could not fetch JSR GraffitiSouls`, err);
res
.status(500)
.json({message: "Failed to fetch JSR GraffitiSouls", err: err});
}
};

export const getJSRFGraffitiSouls = async (req, res) => {
try {
res.send(await fetchJSRFSouls(req));
} catch (err) {
LOGGER.error(`Could not fetch JSRF GraffitiSouls`, err);
res
.status(500)
.json({message: "Failed to fetch JSRF GraffitiSouls", err: err});
}
};

export const getJSRGraffitiSoulById = async (req, res) => {
try {
const soulId = req?.params?.id;
res.send(
await performDBAction(Actions.fetchById, JSR_DB, GraffitiSoul, soulId)
);
} catch (err) {
LOGGER.error(`Could not fetch JSR GraffitiSoul With ID: ${soulId}`, err);
res
.status(500)
.json({message: "Failed to fetch JSR GraffitiSoul By ID", err: err});
}
};

export const getJSRFGraffitiSoulById = async (req, res) => {
try {
const soulId = req?.params?.id;
res.send(
await performDBAction(Actions.fetchById, JSRF_DB, GraffitiSoul, soulId)
);
} catch (err) {
LOGGER.error(`Could not fetch JSRF GraffitiSoul With ID: ${soulId}`, err);
res
.status(500)
.json({message: "Failed to fetch JSRF GraffitiSoul By ID", err: err});
}
};

export const fetchJSRSouls = async (req) => {
if (req?.query) {
return await performDBAction(
Actions.fetchWithQuery,
JSR_DB,
GraffitiSoul,
null,
req?.query
);
}
return await performDBAction(Actions.fetchAll, JSR_DB, GraffitiSoul, null);
};

export const fetchJSRFSouls = async (req) => {
if (req?.query) {
return await performDBAction(
Actions.fetchWithQuery,
JSRF_DB,
GraffitiSoul,
null,
req?.query
);
}
return await performDBAction(Actions.fetchAll, JSRF_DB, GraffitiSoul, null);
};
6 changes: 3 additions & 3 deletions src/controllers/utilController.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ import {Actions} from "../config/dbActions.js";
import Constants from "../constants/dbConstants.js";
import LOGGER from "../utils/logger.js";

const {JSR_DB, JSRF_DB, BRC_DB, gameMap} = Constants;
const {JSR_DB, JSRF_DB, BRC_DB, gameMap, CORE_DB} = Constants;

/* Helper Functions to support all other Controllers */
export const fetchRandom = async (req, resource, game) => {
try {
const games =
const games = game === 'N/A' ? [CORE_DB] :
resource === "Audio" ? [JSR_DB, JSRF_DB] : [JSR_DB, JSRF_DB, BRC_DB];
const selectedGame = req?.query?.game;
const count = Number(req?.query?.count);
const safeCount = Number.isFinite(count) && count > 0 ? count : 1;

/* if a game is provided */
if (game || selectedGame) {
if ((game && game !== 'N/A') || selectedGame) {
const dbName = game || gameMap[selectedGame];
return await performDBAction(
Actions.fetchRandom,
Expand Down
2 changes: 1 addition & 1 deletion src/managers/MiddlewareManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ const cacheMiddleware = (req, res, next) => {
}
res.sendResponse = res.send;
res.send = (body) => {
cache.put(cacheKey, body, 3600000); // 1 hour cache time, restart the service to bypass
cache.put(cacheKey, body, 3600000); // 1 hour cache time, restart the service to bypass or run /cache/clear
res.sendResponse(body);
};
LOGGER.info(`Cache missed for url ${req.url}`);
Expand Down
27 changes: 25 additions & 2 deletions src/public/docs.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ <h2>Resources</h2>
<li>Locations</li>
<li>Levels</li>
<li>Graffiti Tags</li>
<li>Graffiti Souls</li>
<li>Songs</li>
<li>Artists</li>
<li>Collectibles</li>
</ul>
<p>The following Resources will be added in a future release of JSRAPI.</p>
<p>If you would like to contribute in adding any of these resources, see the Contributing Docs.</p>
<ul>
<li>Graffiti Souls</li>
<li>Street Challenges</li>
<li>Jet Challenges</li>
<li>Gangs</li>
Expand Down Expand Up @@ -154,6 +154,29 @@ <h2>Graffiti-Tags</h2>
<pre id="graffiti-tag-response" class="expandable code-snippet" style="display: none;"></pre>
</div>

<div class="main-container">
<h2>Graffiti-Souls</h2>
<p>A Graffiti Soul is a resource describing a Graffiti Soul from a specific Game.</p>
<p>Endpoints:</p>
<ul>
<li><code class="code-snippet">/graffiti-souls</code> ==> Returns all Graffiti-Souls</li>
<li><code class="code-snippet">/graffiti-souls/random</code> ==> Returns a Random Location From any Game</li>
<li><code class="code-snippet">/graffiti-souls/random?game=jsr</code> ==> Returns a Random JSR Graffiti-Souls</li>
<li><code class="code-snippet">/graffiti-souls/random?game=jsrf</code> ==> Returns a Random JSRF Graffiti-Souls</li>
<li><code class="code-snippet">/graffiti-souls/random?count=10</code> ==> Returns 10 random Graffiti-Souls</li>
<li><code class="code-snippet">/graffiti-souls/jsr</code> ==> Returns all Jet Set Radio Graffiti-Souls</li>
<li><code class="code-snippet">/graffiti-souls/jsr/:id</code> ==> Returns a single JSR Graffiti-Soul by ID</li>
<li><code class="code-snippet">/graffiti-souls/jsrf</code> ==> Returns all Jet Set Radio Future Graffiti-Souls</li>
<li><code class="code-snippet">/graffiti-souls/jsrf/:id</code> ==> Returns a single JSRF Graffiti-Soul by ID</li>
<li><code class="code-snippet">/graffiti-souls/jsrf?size=M</code> ==> Returns all JSRF M Graffiti-Souls</li>
<li><code class="code-snippet">/graffiti-souls?locationName=Hikage Street</code> ==> Returns All Graffiti-Souls in a specific location</li>
</ul>
<p>Example Request:</p>
<code class="code-snippet">https://jetsetradio-api.onrender.com/v1/api/graffiti-souls?sortBy=size&orderBy=desc</code>
<p>Example Response: <button class="expandable-button">Expand</button> </p>
<pre id="graffiti-soul-response" class="expandable code-snippet" style="display: none;"></pre>
</div>

<div class="main-container">
<h2>Collectibles</h2>
<p>A Collectible is a resource describing a location or place from a specific Game. The only supported game currently is Bomb Rush Cyberfunk.</p>
Expand Down Expand Up @@ -260,7 +283,7 @@ <h2>Sorting</h2>

<div class="main-container">
<h2>Limiting</h2>
<p>All API routes support the <code class="code-snippet">limit</code> parameter. You can use this to return a limited number of results instead of the entire response.</p>
<p>All API routes(with the exception of /random) support the <code class="code-snippet">limit</code> parameter. You can use this to return a limited number of results instead of the entire response.</p>
<p>Example:</p>
<code class="code-snippet">https://jetsetradio-api.onrender.com/v1/api/locations/jsrf?limit=5</code>
</div>
Expand Down
6 changes: 6 additions & 0 deletions src/public/js/apiTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ export function createApiTable() {
endpoint: "/v1/api/graffitiTags?size=L",
description: "Returns all Large Graffiti Tags from any game",
},
{
endpoint:
"/v1/api/graffiti-souls/jsrf?locationId=64c95601b33c6b029d936802",
description:
"Returns all Graffiti-Souls from The Skyscraper District Location",
},
{
endpoint: "/v1/api/collectibles?type=Outfit",
description: "Returns all Outfit collectibles from Bomb Rush Cyberfunk",
Expand Down
2 changes: 2 additions & 0 deletions src/public/js/docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import characterResource from "./examples/characterExample.js";
import locationResource from "./examples/locationExample.js";
import levelResource from "./examples/levelExample.js";
import graffitiTagResource from "./examples/graffitiTagExample.js";
import graffitiSoulResource from "./examples/graffitiSoulExample.js";
import songResource from "./examples/songExample.js";
import artistResource from "./examples/artistExample.js";
import collectibleResource from "./examples/collectibleExample.js";
Expand All @@ -14,6 +15,7 @@ const resources = [
{selector: "#location-response", data: locationResource},
{selector: "#level-response", data: levelResource},
{selector: "#graffiti-tag-response", data: graffitiTagResource},
{selector: "#graffiti-soul-response", data: graffitiSoulResource},
{selector: "#song-response", data: songResource},
{selector: "#artist-response", data: artistResource},
{selector: "#collectible-response", data: collectibleResource},
Expand Down
13 changes: 13 additions & 0 deletions src/public/js/examples/graffitiSoulExample.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const graffitiSoulResource = {
gameName: "JSRF",
gameId: "64285b7918c8a0231136dc5d",
category: "XL",
number: "No. 131 - Megaro",
size: "XL",
graffitiTagId: "64c955f2b33c6b029d93675a",
locationName: "Hikage Street",
locationId: "64c95601b33c6b029d9367fc",
description: "Complete the Street Challenges grind combo challenge.",
};

export default graffitiSoulResource;
3 changes: 2 additions & 1 deletion src/routes/artistRouter.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import express from 'express';

import { getArtists, getArtistById, getSongsByArtist } from '../controllers/artistController.js';
import { getArtists, getRandomArtist, getArtistById, getSongsByArtist } from '../controllers/artistController.js';


const artists = express.Router();

artists.get('/', async (req, res) => /* #swagger.tags = ['Artists'] */ await getArtists(req, res));
artists.get('/random', async (req, res) => /* #swagger.tags = ['Artists'] */ await getRandomArtist(req, res));
artists.get('/:id', async (req, res) => /* #swagger.tags = ['Artists'] */ await getArtistById(req, res));
artists.get('/:id/songs', async (req, res) => /* #swagger.tags = ['Artists'] */ await getSongsByArtist(req, res));

Expand Down
53 changes: 53 additions & 0 deletions src/routes/graffitiSoulRouter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import express from "express";
import {
getAllGraffitiSouls,
getRandomGraffitiSoul,
getJSRGraffitiSouls,
getJSRGraffitiSoulById,
getJSRFGraffitiSouls,
getJSRFGraffitiSoulById,
} from "../controllers/graffitiSoulController.js";

const graffitiSouls = express.Router();

graffitiSouls.get(
"/",
async (req, res) =>
/* #swagger.tags = ['GraffitiSouls'] */ await getAllGraffitiSouls(req, res)
);
graffitiSouls.get(
"/random",
async (req, res) =>
/* #swagger.tags = ['GraffitiSouls'] */ await getRandomGraffitiSoul(
req,
res
)
);
graffitiSouls.get(
"/jsr",
async (req, res) =>
/* #swagger.tags = ['GraffitiSouls'] */ await getJSRGraffitiSouls(req, res)
);
graffitiSouls.get(
"/jsr/:id",
async (req, res) =>
/* #swagger.tags = ['GraffitiSouls'] */ await getJSRGraffitiSoulById(
req,
res
)
);
graffitiSouls.get(
"/jsrf",
async (req, res) =>
/* #swagger.tags = ['GraffitiSouls'] */ await getJSRFGraffitiSouls(req, res)
);
graffitiSouls.get(
"/jsrf/:id",
async (req, res) =>
/* #swagger.tags = ['GraffitiSouls'] */ await getJSRFGraffitiSoulById(
req,
res
)
);

export default graffitiSouls;
2 changes: 2 additions & 0 deletions src/routes/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import express from "express";

import games from "./gameRouter.js";
import graffitiTags from "./graffitiTagRouter.js";
import graffitiSouls from "./graffitiSoulRouter.js";
import songs from "./songRouter.js";
import artists from "./artistRouter.js";
import characters from "./characterRouter.js";
Expand All @@ -18,6 +19,7 @@ router.use("/games", games);
router.use("/songs", songs);
router.use("/artists", artists);
router.use("/graffiti-tags", graffitiTags);
router.use("/graffiti-souls", graffitiSouls);
router.use("/characters", characters);
router.use("/locations", locations);
router.use("/levels", levels);
Expand Down
Loading