Using GraphQL SDL to generate the TypeScript Types

This commit is contained in:
DrMint 2022-01-02 03:59:54 +01:00
parent 3d2cf6d594
commit 702dc74ab6
12 changed files with 4441 additions and 1455 deletions

View File

@ -1,11 +1,8 @@
import { ChronologyItemEntity, Maybe } from "graphql/operations-types";
import styles from "styles/Chronology/ChronologyItemComponent.module.css"; import styles from "styles/Chronology/ChronologyItemComponent.module.css";
import {
ChronologyItem,
ChronologyItemsEvent,
} from "queries/chronology/overview";
export type ChronologyItemComponentProps = { export type ChronologyItemComponentProps = {
item: ChronologyItem; item: ChronologyItemEntity;
displayYear: boolean; displayYear: boolean;
}; };
@ -14,9 +11,8 @@ export default function ChronologyItemComponent(
): JSX.Element { ): JSX.Element {
function generateAnchor( function generateAnchor(
year: number, year: number,
month: number, month: Maybe<number> | undefined,
day: number, day: Maybe<number> | undefined
event?: number
): string { ): string {
let result: string = ""; let result: string = "";
result += year; result += year;
@ -25,7 +21,10 @@ export default function ChronologyItemComponent(
return result; return result;
} }
function generateYear(displayed_date: string, year: number): string { function generateYear(
displayed_date: Maybe<string> | undefined,
year: number
): string {
if (displayed_date) { if (displayed_date) {
return displayed_date; return displayed_date;
} else { } else {
@ -33,7 +32,10 @@ export default function ChronologyItemComponent(
} }
} }
function generateDate(month: number, day: number): string { function generateDate(
month: Maybe<number> | undefined,
day: Maybe<number> | undefined
): string {
let lut = [ let lut = [
"Jan", "Jan",
"Feb", "Feb",
@ -60,64 +62,84 @@ export default function ChronologyItemComponent(
return result; return result;
} }
return ( if (props.item && props.item.attributes) {
<div return (
className={styles.chronologyItem} <div
id={generateAnchor( className={styles.chronologyItem}
props.item.attributes.year, id={generateAnchor(
props.item.attributes.month, props.item.attributes.year,
props.item.attributes.day props.item.attributes.month,
)} props.item.attributes.day
> )}
{props.displayYear ? ( >
<p className={styles.year}> {props.displayYear ? (
{generateYear( <p className={styles.year}>
props.item.attributes.displayed_date, {generateYear(
props.item.attributes.year props.item.attributes.displayed_date,
)} props.item.attributes.year
)}
</p>
) : (
""
)}
<p className={styles.date}>
{generateDate(props.item.attributes.month, props.item.attributes.day)}
</p> </p>
) : (
""
)}
<p className={styles.date}> <div className={styles.events}>
{generateDate(props.item.attributes.month, props.item.attributes.day)} {props.item.attributes.events?.map((event) => {
</p> if (event) {
return (
<div className={styles.event} key={event.id}>
{event.translations?.map((translation) => {
if (translation)
return (
<>
{translation.title ? (
<h3>{translation.title}</h3>
) : (
""
)}
<div className={styles.events}> {translation.description ? (
{props.item.attributes.events.map((event: ChronologyItemsEvent) => ( <p
<div className={styles.event} key={event.id}> className={
{event.translations.map((translation) => ( event.translations &&
<> event.translations.length > 1
{translation.title ? <h3>{translation.title}</h3> : ""} ? styles.bulletItem
: ""
}
>
{translation.description}
</p>
) : (
""
)}
{translation.note ? (
<em>{"Notes: " + translation.note}</em>
) : (
""
)}
</>
);
})}
{translation.description ? ( <p className={styles.source}>
<p {event.source &&
className={ event.source.data &&
event.translations.length > 1 ? styles.bulletItem : "" event.source.data.attributes
} ? "(" + event.source.data.attributes.name + ")"
> : "(WARNING: NO SOURCE!)"}
{translation.description}
</p> </p>
) : ( </div>
"" );
)} }
{translation.note ? ( })}
<em>{"Notes: " + translation.note}</em> </div>
) : (
""
)}
</>
))}
<p className={styles.source}>
{event.source.data
? "(" + event.source.data.attributes.name + ")"
: "(WARNING: NO SOURCE!)"}
</p>
</div>
))}
</div> </div>
</div> );
); } else {
return <></>;
}
} }

View File

@ -1,9 +1,10 @@
import styles from "styles/Chronology/ChronologyYearComponent.module.css"; import styles from "styles/Chronology/ChronologyYearComponent.module.css";
import { ChronologyItem } from "queries/chronology/overview";
import ChronologyItemComponent from "components/Chronology/ChronologyItemComponent"; import ChronologyItemComponent from "components/Chronology/ChronologyItemComponent";
import { ChronologyItemEntity } from "graphql/operations-types";
type ChronologyYearComponentProps = { type ChronologyYearComponentProps = {
items: ChronologyItem[]; year: number;
items: ChronologyItemEntity[];
}; };
export default function ChronologyYearComponent( export default function ChronologyYearComponent(
@ -12,13 +13,9 @@ export default function ChronologyYearComponent(
return ( return (
<div <div
className={styles.chronologyYear} className={styles.chronologyYear}
id={ id={props.items.length > 1 ? props.year.toString() : undefined}
props.items.length > 1
? props.items[0].attributes.year.toString()
: undefined
}
> >
{props.items.map((item: ChronologyItem, index: number) => ( {props.items.map((item: ChronologyItemEntity, index: number) => (
<ChronologyItemComponent <ChronologyItemComponent
key={index} key={index}
item={item} item={item}

View File

@ -1,11 +1,11 @@
import styles from "styles/Library/LibraryItemComponent.module.css"; import styles from "styles/Library/LibraryItemComponent.module.css";
import { LibraryItem } from "queries/library";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import { BasicDate, getAssetURL } from "queries/helpers"; import { BasicDate, getAssetURL } from "queries/helpers";
import { LibraryItemEntity } from "graphql/operations-types";
export type LibraryItemComponentProps = { export type LibraryItemComponentProps = {
item: LibraryItem; item: LibraryItemEntity;
}; };
export default function LibraryItemComponent( export default function LibraryItemComponent(

View File

@ -5,7 +5,7 @@ import Link from "next/link";
type NavOptionProps = { type NavOptionProps = {
url: string; url: string;
icon?: string; icon?: string;
title: string; title: string | null | undefined;
subtitle?: string; subtitle?: string;
border?: boolean; border?: boolean;
}; };

View File

@ -0,0 +1,103 @@
query getEras($language_code: String) {
chronologyEras {
data {
id
attributes {
slug
starting_year
ending_year
title(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
}
query getChronologyItems($language_code: String) {
chronologyItems(
pagination: { limit: -1 }
sort: ["year:asc", "month:asc", "day:asc"]
) {
data {
id
attributes {
year
month
day
displayed_date
events {
id
source {
data {
attributes {
name
}
}
}
translations(
filters: { language: { code: { eq: $language_code } } }
) {
title
description
note
status
}
}
}
}
}
}
query getLibraryItemsPreview($language_code: String) {
libraryItems(
filters: { root_item: { eq: true } }
pagination: { limit: -1 }
sort: ["slug:asc"]
) {
data {
id
attributes {
title
subtitle
slug
thumbnail {
data {
attributes {
name
alternativeText
caption
width
height
url
}
}
}
release_date {
year
month
day
}
price {
amount
currency {
data {
attributes {
symbol
code
}
}
}
}
size {
width
height
thickness
}
descriptions(filters: { language: { code: { eq: $language_code } } }) {
description
}
}
}
}
}

File diff suppressed because it is too large Load Diff

63
src/graphql/operations.ts Normal file
View File

@ -0,0 +1,63 @@
import {
GetChronologyItemsQuery,
GetChronologyItemsQueryVariables,
GetErasQuery,
GetErasQueryVariables,
GetLibraryItemsPreviewQuery,
GetLibraryItemsPreviewQueryVariables,
} from "graphql/operations-types";
const graphQL = async (query: string, variables?: string) => {
const res = await fetch(process.env.URL_GRAPHQL, {
method: "POST",
body: JSON.stringify({
query: query,
variables: variables,
}),
headers: {
"content-type": "application/json",
Authorization: "Bearer " + process.env.ACCESS_TOKEN,
},
});
return (await res.json()).data;
};
function getQueryFromOperations(queryName: string): string {
const fs = require("fs");
const operations: string = fs.readFileSync(
"./src/graphql/operation.graphql",
"utf8"
);
let startingIndex = -1;
let endingIndex = -1;
const lines = operations.split("\n");
lines.map((line, index) => {
if (startingIndex === -1) {
if (line.startsWith(`query ${queryName}`)) startingIndex = index;
} else if (endingIndex === -1) {
if (line.startsWith("query")) endingIndex = index;
}
});
return lines.slice(startingIndex, endingIndex).join("\n");
}
export async function getEras(
variables: GetErasQueryVariables
): Promise<GetErasQuery> {
const query = getQueryFromOperations("getEras");
return await graphQL(query, JSON.stringify(variables));
}
export async function getChronologyItems(
variables: GetChronologyItemsQueryVariables
): Promise<GetChronologyItemsQuery> {
const query = getQueryFromOperations("getChronologyItems");
return await graphQL(query, JSON.stringify(variables));
}
export async function getLibraryItemsPreview(
variables: GetLibraryItemsPreviewQueryVariables
): Promise<GetLibraryItemsPreviewQuery> {
const query = getQueryFromOperations("getLibraryItemsPreview");
return await graphQL(query, JSON.stringify(variables));
}

2713
src/graphql/schema.graphql Normal file

File diff suppressed because it is too large Load Diff

View File

@ -4,19 +4,18 @@ import SubPanel from "components/Panels/SubPanel";
import ReturnButton from "components/Panels/ReturnButton"; import ReturnButton from "components/Panels/ReturnButton";
import NavOption from "components/Panels/NavOption"; import NavOption from "components/Panels/NavOption";
import ChronologyYearComponent from "components/Chronology/ChronologyYearComponent"; import ChronologyYearComponent from "components/Chronology/ChronologyYearComponent";
import {
getChronologyItems,
getChronologyEras,
ChronologyItem,
ChronologyItemsEvent,
} from "queries/chronology/overview";
import { applyCustomAppProps } from "pages/_app"; import { applyCustomAppProps } from "pages/_app";
import { ChronologyEraEntityResponseCollection } from "queries/types"; import {
ChronologyItemEntity,
GetChronologyItemsQuery,
GetErasQuery,
} from "graphql/operations-types";
import { getEras, getChronologyItems } from "graphql/operations";
type Props = { interface Props {
chronologyItems: ChronologyItem[]; chronologyItems: GetChronologyItemsQuery;
chronologyEras: ChronologyEraEntityResponseCollection; chronologyEras: GetErasQuery;
}; }
applyCustomAppProps(ChronologyOverview, { applyCustomAppProps(ChronologyOverview, {
useSubPanel: true, useSubPanel: true,
@ -25,68 +24,66 @@ applyCustomAppProps(ChronologyOverview, {
export default function ChronologyOverview(props: Props): JSX.Element { export default function ChronologyOverview(props: Props): JSX.Element {
// Group by year the Chronology items // Group by year the Chronology items
let chronologyItemYearGroups: ChronologyItem[][] = [];
props.chronologyItems.map((item: ChronologyItem) => { let chronologyItemYearGroups: ChronologyItemEntity[][] = [];
if (!chronologyItemYearGroups.hasOwnProperty(item.attributes.year)) {
chronologyItemYearGroups[item.attributes.year] = [item]; if (props.chronologyItems.chronologyItems) {
} else { props.chronologyItems.chronologyItems.data.map((item) => {
chronologyItemYearGroups[item.attributes.year].push(item); if (item && item.attributes) {
} if (!chronologyItemYearGroups.hasOwnProperty(item.attributes.year)) {
}); chronologyItemYearGroups[item.attributes.year] = [item];
} else {
chronologyItemYearGroups[item.attributes.year].push(item);
}
}
});
}
return ( return (
<> <>
<SubPanel> <SubPanel>
<ReturnButton url="/chronology" title="Chronology" /> <ReturnButton url="/chronology" title="Chronology" />
<hr /> <hr />
{props.chronologyEras.data.map((era) => ( {props.chronologyEras.chronologyEras
<NavOption ? props.chronologyEras.chronologyEras.data.map((era) => (
key={era.id} <>
url={"#" + era.attributes.slug} {era.attributes && era.attributes.title ? (
title={ <NavOption
era.attributes.title.length ? era.attributes.title[0].title : "" key={era.id}
} url={"#" + era.attributes.slug}
subtitle={ title={
era.attributes.starting_year + " → " + era.attributes.ending_year era.attributes.title[0]
} ? era.attributes.title[0].title
border={true} : ""
/> }
))} subtitle={
era.attributes.starting_year +
" → " +
era.attributes.ending_year
}
border={true}
/>
) : (
""
)}
</>
))
: ""}
</SubPanel> </SubPanel>
<ContentPanel> <ContentPanel>
{props.chronologyItems.map((item: ChronologyItem) => { {chronologyItemYearGroups.map((items, index: number) => {
if (!item.attributes.year) if (items && items[0].attributes?.year) {
console.warn("Missing year on ChronologyItem (" + item.id + ")"); return (
item.attributes.events.map((event: ChronologyItemsEvent) => { <ChronologyYearComponent
if (!event.source.data) { key={index}
console.warn( year={items[0].attributes?.year}
"Missing Source on ChronologyItem (" + items={items}
item.id + />
"), Event (" + );
event.id +
")"
);
}
if (event.translations.length < 1) {
console.warn(
"Missing Translation on ChronologyItem (" +
item.id +
"), Event (" +
event.id +
")"
);
}
});
})}
{chronologyItemYearGroups.map(
(items: ChronologyItem[], index: number) => {
return <ChronologyYearComponent key={index} items={items} />;
} }
)} })}
</ContentPanel> </ContentPanel>
</> </>
); );
@ -95,8 +92,10 @@ export default function ChronologyOverview(props: Props): JSX.Element {
export const getStaticProps: GetStaticProps = async (context) => { export const getStaticProps: GetStaticProps = async (context) => {
return { return {
props: { props: {
chronologyItems: await getChronologyItems(context.locale), chronologyItems: await getChronologyItems({
chronologyEras: await getChronologyEras(context.locale), language_code: context.locale,
}),
chronologyEras: await getEras({ language_code: context.locale }),
}, },
}; };
}; };

View File

@ -1,12 +1,13 @@
import { GetStaticProps } from "next"; import { GetStaticProps } from "next";
import SubPanel from "components/Panels/SubPanel"; import SubPanel from "components/Panels/SubPanel";
import ContentPanel from "components/Panels/ContentPanel"; import ContentPanel from "components/Panels/ContentPanel";
import { LibraryItem, getLibraryItems } from "queries/library/index";
import LibraryItemComponent from "components/Library/LibraryItemComponent"; import LibraryItemComponent from "components/Library/LibraryItemComponent";
import { applyCustomAppProps } from "pages/_app"; import { applyCustomAppProps } from "pages/_app";
import { GetLibraryItemsPreviewQuery } from "graphql/operations-types";
import { getLibraryItemsPreview } from "graphql/operations";
type Props = { type Props = {
libraryItems: LibraryItem[]; libraryItems: GetLibraryItemsPreviewQuery;
}; };
applyCustomAppProps(Library, { applyCustomAppProps(Library, {
@ -15,6 +16,8 @@ applyCustomAppProps(Library, {
}); });
export default function Library(props: Props): JSX.Element { export default function Library(props: Props): JSX.Element {
console.log(props);
return ( return (
<> <>
<SubPanel> <SubPanel>
@ -29,7 +32,7 @@ export default function Library(props: Props): JSX.Element {
</SubPanel> </SubPanel>
<ContentPanel> <ContentPanel>
{props.libraryItems.map((item: LibraryItem) => ( {props.libraryItems.libraryItems?.data.map((item) => (
<LibraryItemComponent key={item.id} item={item} /> <LibraryItemComponent key={item.id} item={item} />
))} ))}
</ContentPanel> </ContentPanel>
@ -40,7 +43,7 @@ export default function Library(props: Props): JSX.Element {
export const getStaticProps: GetStaticProps = async (context) => { export const getStaticProps: GetStaticProps = async (context) => {
return { return {
props: { props: {
libraryItems: await getLibraryItems(context.locale), libraryItems: await getLibraryItemsPreview({language_code: context.locale}),
}, },
}; };
}; };

View File

@ -1,109 +0,0 @@
import { queryGraphQL } from "queries/helpers";
import { Source } from "queries/helpers";
import { ChronologyEraEntityResponseCollection } from "queries/types";
export type ChronologyItem = {
id: string;
attributes: {
year: number;
month: number;
day: number;
displayed_date: string;
events: ChronologyItemsEvent[];
};
};
export type ChronologyItemsEvent = {
id: string;
source: Source;
translations: {
title: string;
description: string;
note: string;
status: string;
}[];
};
export async function getChronologyItems(
language_code: string | undefined
): Promise<ChronologyItem[]> {
return (
await queryGraphQL(
`
{
chronologyItems (pagination: {limit: -1}, sort: ["year:asc", "month:asc", "day:asc"]) {
data {
id
attributes {
year
month
day
displayed_date
events {
id
source {
data {
attributes {
name
}
}
}
translations(filters: { language: { code: { eq: "${language_code}" } } }) {
title
description
note
status
}
}
}
}
}
}
`
)
).chronologyItems.data;
}
/*
export type ChronologyEra = {
id: string;
attributes: ChronologyEraAttributes;
};
export type ChronologyEraAttributes = {
slug: string;
starting_year: number;
ending_year: number;
title: ChronologyEraTranslation[];
};
export type ChronologyEraTranslation = {
title: string;
};
*/
export async function getChronologyEras(
language_code: string | undefined
): Promise<ChronologyEraEntityResponseCollection> {
return (
await queryGraphQL(
`
{
chronologyEras {
data {
id
attributes {
slug
starting_year
ending_year
title (filters: {language: {code: {eq: "${language_code}"}}}){
title
}
}
}
}
}
`
)
).chronologyEras;
}

View File

@ -1,8 +1,9 @@
export const queryGraphQL = async (query: String) => { export const queryGraphQL = async (query: string, variables?: string) => {
const res = await fetch(process.env.URL_GRAPHQL, { const res = await fetch(process.env.URL_GRAPHQL, {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
query: query, query: query,
variables: variables
}), }),
headers: { headers: {
"content-type": "application/json", "content-type": "application/json",
@ -12,17 +13,6 @@ export const queryGraphQL = async (query: String) => {
return (await res.json()).data; return (await res.json()).data;
}; };
export const queryGraphQLSystem = async (query: string) => {
const res = await fetch(
process.env.URL_GRAPHQL_SYSTEM +
"?access_token=" +
process.env.ACCESS_TOKEN +
"&query=" +
query
);
return (await res.json()).data;
};
export function getAssetURL(url: string): string { export function getAssetURL(url: string): string {
return process.env.NEXT_PUBLIC_URL_CMS + url; return process.env.NEXT_PUBLIC_URL_CMS + url;
} }