APIs (#3)
* Initial RESTful API * RESTful API tests * Scrapping error handling refactored * Initial GraphQL API * GraphQL API tests
This commit is contained in:
@@ -2,7 +2,7 @@ import { FC } from "react";
|
||||
import { CustomError } from "../components";
|
||||
|
||||
const My404: FC = () => (
|
||||
<CustomError statusCode={404} />
|
||||
<CustomError statusCode={404} statusText={"This page could not be found"} />
|
||||
);
|
||||
|
||||
export default My404;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { FC } from "react";
|
||||
import { CustomError } from "../components";
|
||||
|
||||
const My500: FC = () => (
|
||||
<CustomError statusCode={500} />
|
||||
<CustomError statusCode={500} statusText={"Internal Server Error"} />
|
||||
);
|
||||
|
||||
export default My500;
|
||||
|
||||
@@ -3,13 +3,13 @@ import { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from "next";
|
||||
import Router from "next/router";
|
||||
import { Stack, VStack, HStack, IconButton } from "@chakra-ui/react";
|
||||
import { FaExchangeAlt } from "react-icons/fa";
|
||||
import { CustomError, Layout, LangSelect, TranslationArea } from "../components";
|
||||
import { Layout, LangSelect, TranslationArea } from "../components";
|
||||
import { useToastOnLoad } from "../hooks";
|
||||
import { googleScrape, extractSlug, textToSpeechScrape } from "../utils/translate";
|
||||
import { retrieveFiltered, replaceBoth } from "../utils/language";
|
||||
import langReducer, { Actions, initialState } from "../utils/reducer";
|
||||
|
||||
const Page: FC<InferGetStaticPropsType<typeof getStaticProps>> = ({ home, translationRes, audio, statusCode, errorMsg, initial }) => {
|
||||
const Page: FC<InferGetStaticPropsType<typeof getStaticProps>> = ({ home, translationRes, audio, errorMsg, initial }) => {
|
||||
const [{ source, target, query, delayedQuery, translation }, dispatch] = useReducer(langReducer, initialState);
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLTextAreaElement | HTMLSelectElement>) => {
|
||||
@@ -55,9 +55,7 @@ const Page: FC<InferGetStaticPropsType<typeof getStaticProps>> = ({ home, transl
|
||||
updateDeps: initial
|
||||
});
|
||||
|
||||
return statusCode ? (
|
||||
<CustomError statusCode={statusCode} />
|
||||
) : (
|
||||
return (
|
||||
<Layout home={home}>
|
||||
<VStack px={[8, null, 24, 40]} w="full">
|
||||
<HStack px={[1, null, 3, 4]} w="full">
|
||||
@@ -160,7 +158,7 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||
source, target, query
|
||||
}
|
||||
},
|
||||
revalidate: !textScrape.errorMsg && !textScrape.statusCode
|
||||
revalidate: !textScrape.errorMsg
|
||||
? 2 * 30 * 24 * 60 * 60 // 2 months
|
||||
: 1
|
||||
};
|
||||
|
||||
80
pages/api/graphql.ts
Normal file
80
pages/api/graphql.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { ApolloServer, gql, IResolvers } from "apollo-server-micro";
|
||||
import { NextApiHandler } from "next";
|
||||
import NextCors from "nextjs-cors";
|
||||
import { googleScrape, textToSpeechScrape } from "../../utils/translate";
|
||||
|
||||
export const typeDefs = gql`
|
||||
type Query {
|
||||
translation(source: String="auto" target: String="en" query: String!): Translation!
|
||||
audio(lang: String! query: String!): Entry!
|
||||
}
|
||||
type Translation {
|
||||
source: Entry!
|
||||
target: Entry!
|
||||
}
|
||||
type Entry {
|
||||
lang: String!
|
||||
text: String
|
||||
audio: [Int]
|
||||
}
|
||||
`;
|
||||
|
||||
export const resolvers: IResolvers = {
|
||||
Query: {
|
||||
translation(_, args) {
|
||||
const { source, target, query } = args;
|
||||
return {
|
||||
source: {
|
||||
lang: source,
|
||||
text: query
|
||||
},
|
||||
target: {
|
||||
lang: target
|
||||
}
|
||||
};
|
||||
},
|
||||
audio(_, args) {
|
||||
return {
|
||||
lang: args.lang,
|
||||
text: args.query
|
||||
};
|
||||
}
|
||||
},
|
||||
Translation: {
|
||||
async target(parent) {
|
||||
const { source, target } = parent;
|
||||
const { translationRes } = await googleScrape(source.lang, target.lang, source.text);
|
||||
return {
|
||||
lang: target.lang,
|
||||
text: translationRes
|
||||
};
|
||||
}
|
||||
},
|
||||
Entry: {
|
||||
async audio(parent) {
|
||||
const { lang, text } = parent;
|
||||
return await textToSpeechScrape(lang, text);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
bodyParser: false
|
||||
}
|
||||
};
|
||||
|
||||
const apolloHandler = new ApolloServer({ typeDefs, resolvers }).createHandler({ path: "/api/graphql" });
|
||||
|
||||
const handler: NextApiHandler = async (req, res) => {
|
||||
await NextCors(req, res, {
|
||||
methods: ["GET", "POST"],
|
||||
origin: "*"
|
||||
});
|
||||
|
||||
return req.method !== "OPTIONS"
|
||||
? apolloHandler(req, res)
|
||||
: res.end();
|
||||
};
|
||||
|
||||
export default handler;
|
||||
49
pages/api/v1/[[...slug]].ts
Normal file
49
pages/api/v1/[[...slug]].ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { NextApiHandler } from "next";
|
||||
import NextCors from "nextjs-cors";
|
||||
import { googleScrape, textToSpeechScrape } from "../../../utils/translate";
|
||||
|
||||
type Data = {
|
||||
translation?: string,
|
||||
audio?: number[],
|
||||
error?: string
|
||||
};
|
||||
|
||||
const methods = ["GET"];
|
||||
|
||||
const handler: NextApiHandler<Data> = async (req, res) => {
|
||||
await NextCors(req, res, {
|
||||
methods,
|
||||
origin: "*",
|
||||
preflightContinue: true
|
||||
});
|
||||
|
||||
const {
|
||||
query: { slug },
|
||||
method
|
||||
} = req;
|
||||
|
||||
if (!slug || !Array.isArray(slug) || slug.length !== 3)
|
||||
return res.status(404).json({ error: "Not Found" });
|
||||
|
||||
if (!method || !methods.includes(method)) {
|
||||
res.setHeader("Allow", methods);
|
||||
return res.status(405).json({ error: "Method Not Allowed" });
|
||||
}
|
||||
|
||||
const [source, target, query] = slug;
|
||||
|
||||
if (source === "audio") {
|
||||
const audio = await textToSpeechScrape(target, query);
|
||||
return audio
|
||||
? res.status(200).json({ audio })
|
||||
: res.status(500).json({ error: "An error occurred while retrieving the audio" });
|
||||
}
|
||||
|
||||
const { translationRes, errorMsg } = await googleScrape(source, target, query);
|
||||
|
||||
if (errorMsg)
|
||||
return res.status(500).json({ error: errorMsg });
|
||||
res.status(200).json({ translation: translationRes });
|
||||
}
|
||||
|
||||
export default handler;
|
||||
Reference in New Issue
Block a user