From bdd8bf78adcb1d1ed3bef1c2727d98ba133adde4 Mon Sep 17 00:00:00 2001 From: thomasgross Date: Fri, 15 Nov 2024 14:14:26 +0100 Subject: [PATCH 01/23] feat: add UniLogin configuration query and update related services --- .env.example | 5 +- __tests__/config.test.ts | 3 +- __tests__/refresh-token.test.ts | 5 + app/layout.tsx | 6 + codegen.ts | 47 +++- lib/config/resolvers/service.unilogin.ts | 52 +++- lib/graphql/generated/dpl-cms/graphql.tsx | 250 ++++++++++++------- lib/graphql/generated/fbi/graphql.tsx | 72 +++--- lib/graphql/queries/uniLogin.dpl-cms.graphql | 10 + lib/session/oauth/uniloginClient.ts | 16 +- package.json | 2 +- tsconfig.json | 4 +- 12 files changed, 320 insertions(+), 152 deletions(-) create mode 100644 lib/graphql/queries/uniLogin.dpl-cms.graphql diff --git a/.env.example b/.env.example index 7f41d35e..93a77cb7 100644 --- a/.env.example +++ b/.env.example @@ -3,9 +3,6 @@ NEXT_PUBLIC_GRAPHQL_SCHEMA_ENDPOINT_FBI=https://temp.fbi-api.dbc.dk/ereolgo/grap // WILL BE REPLACED WITH DYNAMIC TOKEN NEXT_PUBLIC_LIBRARY_TOKEN=XXX +UNILOGIN_BASIC_AUTH_HEADER=XXX UNILOGIN_SESSION_SECRET=XXX -UNILOGIN_API_URL=https://et-broker.unilogin.dk -UNILOGIN_WELKNOWN_URL=https://et-broker.unilogin.dk/auth/realms/broker/.well-known/openid-configuration -UNILOGIN_CLIENT_ID=XXX -UNILOGIN_CLIENT_SECRET=XXXX NEXT_PUBLIC_APP_URL=http://localhost:3000 diff --git a/__tests__/config.test.ts b/__tests__/config.test.ts index a6d668cc..c9e9617b 100644 --- a/__tests__/config.test.ts +++ b/__tests__/config.test.ts @@ -1,9 +1,10 @@ -import { expect, test, vi } from "vitest" +import { expect, test } from "vitest" import goConfig from "@/lib/config/config" import MissingConfigurationError from "@/lib/config/errors/MissingConfigurationError" test("That an error is thrown if we ask for unknown config", async () => { + // @ts-ignore expect(() => goConfig("unknown.thingy")).toThrowError(MissingConfigurationError) }) diff --git a/__tests__/refresh-token.test.ts b/__tests__/refresh-token.test.ts index 7dd31ce4..b1068679 100644 --- a/__tests__/refresh-token.test.ts +++ b/__tests__/refresh-token.test.ts @@ -4,6 +4,7 @@ import { IronSession, getIronSession } from "iron-session" import { testApiHandler } from "next-test-api-route-handler" import { afterAll, beforeAll, beforeEach, expect, test, vi } from "vitest" +// @ts-ignore import * as tokenRefreshHandler from "@/app/auth/token/refresh/route" import { TSessionData, accessTokenShouldBeRefreshed } from "@/lib/session/session" @@ -24,6 +25,7 @@ afterAll(() => { }) beforeEach(() => { + // @ts-ignore getIronSession.mockResolvedValue({ isLoggedIn: true, }) @@ -41,6 +43,7 @@ const sessionThatShouldBeRefreshed = () => ({ test("That the refresh endpoint redirects to the frontpage if there is no active session", async () => { // Simulate an anonymous session. + // @ts-ignore getIronSession.mockResolvedValue({ isLoggedIn: false, }) @@ -57,6 +60,7 @@ test("That the refresh endpoint redirects to the frontpage if there is no active test("That the refresh endpoint redirects to the given endpoint after refreshing token", async () => { // This is an authorized session that should be refreshed. + // @ts-ignore getIronSession.mockResolvedValue(sessionThatShouldBeRefreshed()) await testApiHandler({ @@ -69,6 +73,7 @@ test("That the refresh endpoint redirects to the given endpoint after refreshing }) // This is an authorized session that should NOT be refreshed. + // @ts-ignore getIronSession.mockResolvedValue({ isLoggedIn: true, expires: add(new Date(), { seconds: 300 }), diff --git a/app/layout.tsx b/app/layout.tsx index d08855c0..ae26dd42 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -5,6 +5,8 @@ import Footer from "@/components/global/footer/Footer" import GridHelper from "@/components/global/gridHelper/GridHelper" import Header from "@/components/global/header/Header" import Theme from "@/components/global/theme/Theme" +import { initUniLoginConfiguration } from "@/lib/config/resolvers/service.unilogin" +import getQueryClient from "@/lib/getQueryClient" import ReactQueryProvider from "@/lib/providers/ReactQueryProvider" import "@/styles/globals.css" @@ -40,6 +42,10 @@ export default function RootLayout({ }: Readonly<{ children: React.ReactNode }>) { + // This is a workaround to ensure that the uniloginConfiguration is loaded before the app is rendered + const queryClient = getQueryClient() + initUniLoginConfiguration(queryClient) + return ( diff --git a/codegen.ts b/codegen.ts index 6e765c07..1921e122 100644 --- a/codegen.ts +++ b/codegen.ts @@ -9,6 +9,49 @@ loadEnvConfig(process.cwd()) const config: CodegenConfig = { overwrite: true, generates: { + "lib/graphql/generated/dpl-cms/graphql.tsx": { + documents: "**/*.dpl-cms.graphql", + // TODO: Make this configurable + schema: { + "http://dapple-cms.docker/graphql": { + headers: { + Authorization: `Basic ${goConfig("service.unilogin.basic-auth.header")}`, + }, + }, + }, + plugins: ["typescript", "typescript-operations", "typescript-react-query"], + config: { + futureProofEnums: true, + withHooks: true, + defaultScalarType: "unknown", + reactQueryVersion: 5, + exposeFetcher: true, + exposeQueryKeys: true, + addSuspenseQuery: true, + namingConvention: { + typeNames: "change-case-all#pascalCase", + transformUnderscore: true, + }, + fetcher: { + // TODO: Make this configurable + endpoint: "http://dapple-cms.docker/graphql", + fetchParams: JSON.stringify({ + headers: { + "Content-Type": "application/json", + Authorization: `Basic ${goConfig("service.unilogin.basic-auth.header")}`, + }, + }), + }, + }, + hooks: { + afterOneFileWrite: ["yarn eslint --fix"], + }, + }, + // "lib/graphql/generated/dpl-cms/graphql.schema.json": { + // // TODO: Make this configurable + // schema: "http://dapple-cms.docker/graphql", + // plugins: ["introspection"], + // }, "lib/graphql/generated/fbi/graphql.tsx": { documents: "**/*.fbi.graphql", schema: [ @@ -22,8 +65,8 @@ const config: CodegenConfig = { ], plugins: ["typescript", "typescript-operations", "typescript-react-query"], config: { - // futureProofEnums: true, - // withHooks: true, + futureProofEnums: true, + withHooks: true, defaultScalarType: "unknown", reactQueryVersion: 5, exposeFetcher: true, diff --git a/lib/config/resolvers/service.unilogin.ts b/lib/config/resolvers/service.unilogin.ts index 4f365a94..6e503b4b 100644 --- a/lib/config/resolvers/service.unilogin.ts +++ b/lib/config/resolvers/service.unilogin.ts @@ -1,27 +1,53 @@ +import { QueryClient } from "@tanstack/react-query" + +import { + GetUniLoginConfigurationQuery, + useGetUniLoginConfigurationQuery, +} from "@/lib/graphql/generated/dpl-cms/graphql" + +export let uniloginConfiguration: GetUniLoginConfigurationQuery | undefined + +export const initUniLoginConfiguration = async (queryClient: QueryClient) => { + if (uniloginConfiguration) return uniloginConfiguration + + const data = await queryClient.ensureQueryData({ + queryKey: useGetUniLoginConfigurationQuery.getKey(), + queryFn: useGetUniLoginConfigurationQuery.fetcher(), + }) + + uniloginConfiguration = data + return data +} + const serviceUnilogin = { "service.unilogin.api.url": () => { - if (process.env.UNILOGIN_API_URL) { - return process.env.UNILOGIN_API_URL + if (uniloginConfiguration?.dplConfiguration?.unilogin?.unilogin_api_url) { + return uniloginConfiguration?.dplConfiguration?.unilogin?.unilogin_api_url } }, "service.unilogin.wellknown.url": () => { - if (process.env.UNILOGIN_WELKNOWN_URL) { - return process.env.UNILOGIN_WELKNOWN_URL - } - }, - "service.unilogin.refresh-token.url": () => { - if (process.env.UNILOGIN_REFRESH_TOKEN_URL) { - return process.env.UNILOGIN_REFRESH_TOKEN_URL + if (uniloginConfiguration?.dplConfiguration?.unilogin?.unilogin_api_wellknown_url) { + return uniloginConfiguration?.dplConfiguration?.unilogin?.unilogin_api_wellknown_url } }, "service.unilogin.client-id": () => { - if (process.env.UNILOGIN_CLIENT_ID) { - return process.env.UNILOGIN_CLIENT_ID + if (uniloginConfiguration?.dplConfiguration?.unilogin?.unilogin_api_client_id) { + return uniloginConfiguration?.dplConfiguration?.unilogin?.unilogin_api_client_id } }, "service.unilogin.client-secret": () => { - if (process.env.UNILOGIN_CLIENT_SECRET) { - return process.env.UNILOGIN_CLIENT_SECRET + if (uniloginConfiguration?.dplConfiguration?.unilogin?.unilogin_api_client_secret) { + return uniloginConfiguration?.dplConfiguration?.unilogin?.unilogin_api_client_secret + } + }, + "service.unilogin.basic-auth.header": () => { + if (process.env.UNILOGIN_BASIC_AUTH_HEADER) { + return process.env.UNILOGIN_BASIC_AUTH_HEADER + } + }, + "service.unilogin.refresh-token.url": () => { + if (process.env.UNILOGIN_REFRESH_TOKEN_URL) { + return process.env.UNILOGIN_REFRESH_TOKEN_URL } }, "service.unilogin.session.secret": () => { diff --git a/lib/graphql/generated/dpl-cms/graphql.tsx b/lib/graphql/generated/dpl-cms/graphql.tsx index c5229fce..9ebb4c41 100644 --- a/lib/graphql/generated/dpl-cms/graphql.tsx +++ b/lib/graphql/generated/dpl-cms/graphql.tsx @@ -1,4 +1,4 @@ -import { useQuery, UseQueryOptions, useSuspenseQuery, UseSuspenseQueryOptions } from '@tanstack/react-query'; +import { useQuery, useSuspenseQuery, UseQueryOptions, UseSuspenseQueryOptions } from '@tanstack/react-query'; export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; @@ -11,7 +11,7 @@ function fetcher(query: string, variables?: TVariables) { return async (): Promise => { const res = await fetch("http://dapple-cms.docker/graphql", { method: "POST", - ...({"headers":{"Content-Type":"application/json"}}), + ...({"headers":{"Content-Type":"application/json","Authorization":"Basic Z3JhcGhxbF9jb25zdW1lcjp0ZXN0"}}), body: JSON.stringify({ query, variables }), }); @@ -92,6 +92,12 @@ export type DateTime = { timezone: Scalars['TimeZone']['output']; }; +/** DPL Configuration. */ +export type DplConfiguration = { + __typename?: 'DplConfiguration'; + unilogin?: Maybe; +}; + /** A file object to represent an managed file. */ export type File = { __typename?: 'File'; @@ -163,22 +169,22 @@ export type MediaDocument = MediaInterface & { created: DateTime; /** The Universally Unique IDentifier (UUID). */ id: Scalars['ID']['output']; - /** Sprog */ + /** Language */ langcode: Language; - /** Fil */ + /** File */ mediaFile: File; - /** Navn */ + /** Name */ name: Scalars['String']['output']; /** Alternativ URL */ path: Scalars['String']['output']; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; }; /** Entity type media. */ export type MediaImage = MediaInterface & { __typename?: 'MediaImage'; - /** Bruges til fotokreditering og info om copyright. Vises som regel ved siden af billedet. */ + /** Used for public credit and copyright information. Will usually be displayed along with the media. */ byline?: Maybe; /** The time the media item was last edited. */ changed: DateTime; @@ -186,21 +192,20 @@ export type MediaImage = MediaInterface & { created: DateTime; /** The Universally Unique IDentifier (UUID). */ id: Scalars['ID']['output']; - /** Sprog */ + /** Language */ langcode: Language; /** - * Du kan indstille et fokuspunkt ved at klikke på forhåndsvisningen af - * billedet og flytte det hvide mål.

Ved at indstille et fokuspunkt - * fortæller du systemet, hvilken del af billedet der skal være i fokus, når - * det beskæres.

Brug funktionen "forhåndsvisning" til at se, - * hvordan dit billede vil blive beskåret på tværs af billedstil. + * You can set a focal point, by clicking on the preview on the image, moving the + * white target.

By setting a focal point, you tell the system which + * part of the image to keep in focus when it gets cropped.

Use the + * "preview" function, to see how your image will be cropped across image styles. */ mediaImage: Image; - /** Navn */ + /** Name */ name: Scalars['String']['output']; /** Alternativ URL */ path: Scalars['String']['output']; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; }; @@ -212,13 +217,13 @@ export type MediaInterface = { created: DateTime; /** The Universally Unique IDentifier (UUID). */ id: Scalars['ID']['output']; - /** Sprog */ + /** Language */ langcode: Language; - /** Navn */ + /** Name */ name: Scalars['String']['output']; /** Alternativ URL */ path: Scalars['String']['output']; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; }; @@ -234,15 +239,15 @@ export type MediaVideo = MediaInterface & { created: DateTime; /** The Universally Unique IDentifier (UUID). */ id: Scalars['ID']['output']; - /** Sprog */ + /** Language */ langcode: Language; - /** URL til video */ + /** Remote video URL */ mediaOembedVideo: Scalars['String']['output']; - /** Navn */ + /** Name */ name: Scalars['String']['output']; /** Alternativ URL */ path: Scalars['String']['output']; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; }; @@ -253,19 +258,19 @@ export type Mutation = { _: Scalars['Boolean']['output']; }; -/** Brug artikler til nyhedspræget indhold med en begrænset levetid. */ +/** Use articles for news-worthy content, that does not get updated regularly. */ export type NodeArticle = NodeInterface & { __typename?: 'NodeArticle'; - /** Bibliotek */ + /** Branch */ branch?: Maybe; /** - * Oplys en canonical URL hvis indholdet i artiklen er kopieret fra en anden - * hjemmeside (fx kopieret fra et andet biblioteks hjemmeside). Dette hjælper - * med at signalere til søgemaskiner at kilden til indholdet er den - * specificerede side, og sikrer at den originale kilde krediteres. + * Please provide a canonical URL if this content has been duplicated from other + * websites. This helps signal to search engines that the preferred source of + * this content is the specified canonical URL, preventing potential duplicate + * content issues and ensuring proper attribution to the original source. */ canonicalUrl?: Maybe; - /** Kategorier */ + /** Categories */ categories?: Maybe; /** Tidspunktet hvor indholdselementet sidst blev redigeret. */ changed: DateTime; @@ -273,9 +278,9 @@ export type NodeArticle = NodeInterface & { created: DateTime; /** The Universally Unique IDentifier (UUID). */ id: Scalars['ID']['output']; - /** Sprog */ + /** Language */ langcode: Language; - /** Overskriv forfatter */ + /** Override author */ overrideAuthor?: Maybe; /** Paragraphs */ paragraphs?: Maybe>; @@ -286,26 +291,27 @@ export type NodeArticle = NodeInterface & { /** Publication date */ publicationDate: DateTime; /** - * Som standard er forfatteren sat til den Drupal-bruger, der ejer indholdet.

Hvis du ønsker at tilsidesætte dette med din egen tekst, kan du + * By default, the author is set to the Drupal user that owns the content.

If you want to override this, with a manual text, you can check this. */ showOverrideAuthor?: Maybe; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; /** Klæbrig */ sticky: Scalars['Boolean']['output']; - /** Manchet */ + /** Subtitle */ subtitle?: Maybe; /** Tags */ tags?: Maybe>; /** - * Teaserfelterne bruges til cards som blikfang for indholdet. Hvis der ikke er - * valgt et teaserbillede, vil teksten vises i stedet. + * The teaser fields are used for the card of display.
If no image has been + * selected, the text will be shown instead:




*/ teaserImage?: Maybe; - /** Teasertekst */ + /** Teaser text */ teaserText?: Maybe; - /** Titel */ + /** Title */ title: Scalars['String']['output']; }; @@ -317,17 +323,17 @@ export type NodeInterface = { created: DateTime; /** The Universally Unique IDentifier (UUID). */ id: Scalars['ID']['output']; - /** Sprog */ + /** Language */ langcode: Language; /** Alternativ URL */ path: Scalars['String']['output']; /** Forfremmet til forside */ promote: Scalars['Boolean']['output']; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; /** Klæbrig */ sticky: Scalars['Boolean']['output']; - /** Titel */ + /** Title */ title: Scalars['String']['output']; }; @@ -337,9 +343,9 @@ export type NodeUnion = NodeArticle; /** Entity type paragraph. */ export type ParagraphAccordion = ParagraphInterface & { __typename?: 'ParagraphAccordion'; - /** Accordion beskrivelse */ + /** Accordion description */ accordionDescription?: Maybe; - /** Accordion titel */ + /** Accordion title */ accordionTitle: Text; /** The time that the Paragraph was created. */ created: DateTime; @@ -347,7 +353,7 @@ export type ParagraphAccordion = ParagraphInterface & { id: Scalars['ID']['output']; /** The paragraphs entity language code. */ langcode: Language; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; }; @@ -366,13 +372,13 @@ export type ParagraphBanner = ParagraphInterface & { id: Scalars['ID']['output']; /** The paragraphs entity language code. */ langcode: Language; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; /** Underlined title */ underlinedTitle?: Maybe; }; -/** Vis automatisk alt indhold, som refererer til dit valgte brødkrumme element. */ +/** Automatically display all content that is referencing your chosen breadcrumb item. */ export type ParagraphBreadcrumbChildren = ParagraphInterface & { __typename?: 'ParagraphBreadcrumbChildren'; /** The time that the Paragraph was created. */ @@ -381,7 +387,7 @@ export type ParagraphBreadcrumbChildren = ParagraphInterface & { id: Scalars['ID']['output']; /** The paragraphs entity language code. */ langcode: Language; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; }; @@ -392,13 +398,13 @@ export type ParagraphCardGridAutomatic = ParagraphInterface & { created: DateTime; /** If nothing is selected, all will be chosen. */ filterBranches?: Maybe>; - /** Filter efter kategorier */ + /** Filter by categories */ filterCategories?: Maybe>; /** Condition type */ filterCondType: Scalars['String']['output']; /** If nothing is selected, all will be chosen. */ filterContentTypes?: Maybe>; - /** Filter efter tags */ + /** Filter by tags */ filterTags?: Maybe>; /** The Universally Unique IDentifier (UUID). */ id: Scalars['ID']['output']; @@ -406,9 +412,9 @@ export type ParagraphCardGridAutomatic = ParagraphInterface & { langcode: Language; /** More link */ moreLink?: Maybe; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; - /** Titel */ + /** Title */ title?: Maybe; }; @@ -417,7 +423,7 @@ export type ParagraphCardGridManual = ParagraphInterface & { __typename?: 'ParagraphCardGridManual'; /** The time that the Paragraph was created. */ created: DateTime; - /** Indhold */ + /** Content */ gridContent?: Maybe>; /** The Universally Unique IDentifier (UUID). */ id: Scalars['ID']['output']; @@ -425,16 +431,16 @@ export type ParagraphCardGridManual = ParagraphInterface & { langcode: Language; /** More link */ moreLink?: Maybe; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; - /** Overskrift */ + /** Title */ title?: Maybe; }; /** Entity type paragraph. */ export type ParagraphContentSlider = ParagraphInterface & { __typename?: 'ParagraphContentSlider'; - /** Indhold */ + /** Contents */ contentReferences?: Maybe>; /** The time that the Paragraph was created. */ created: DateTime; @@ -442,7 +448,7 @@ export type ParagraphContentSlider = ParagraphInterface & { id: Scalars['ID']['output']; /** The paragraphs entity language code. */ langcode: Language; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; /** deprecated */ title?: Maybe; @@ -469,7 +475,7 @@ export type ParagraphContentSliderAutomatic = ParagraphInterface & { id: Scalars['ID']['output']; /** The paragraphs entity language code. */ langcode: Language; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; /** deprecated */ title?: Maybe; @@ -477,18 +483,18 @@ export type ParagraphContentSliderAutomatic = ParagraphInterface & { underlinedTitle?: Maybe; }; -/** Denne paragraph viser en liste af arrangementer filtreret på kategori, tags og filialer. */ +/** This paragraph displays a generated a list of events, based on specified filters for tags, categories and branches. */ export type ParagraphFilteredEventList = ParagraphInterface & { __typename?: 'ParagraphFilteredEventList'; /** The time that the Paragraph was created. */ created: DateTime; - /** Tilføj enhver forgrening, du vil inkludere */ + /** If nothing is selected, all will be chosen. */ filterBranches?: Maybe>; - /** Tilføj en kategori, du vil inkludere */ + /** Add a category you want to include */ filterCategories?: Maybe>; /** Condition type */ filterCondType: Scalars['String']['output']; - /** Tilføj et tag, du vil inkludere */ + /** Add a tag you want to include */ filterTags?: Maybe>; /** The Universally Unique IDentifier (UUID). */ id: Scalars['ID']['output']; @@ -500,9 +506,9 @@ export type ParagraphFilteredEventList = ParagraphInterface & { * enough results based on your selected filters. */ maxItemAmount: Scalars['String']['output']; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; - /** Titel */ + /** Title */ title?: Maybe; }; @@ -514,31 +520,31 @@ export type ParagraphInterface = { id: Scalars['ID']['output']; /** The paragraphs entity language code. */ langcode: Language; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; }; -/** Dette afsnit vil vise en liste over arrangementer, der er manuelt valgt. */ +/** This paragraph will show a list of events that are manually selected. */ export type ParagraphManualEventList = ParagraphInterface & { __typename?: 'ParagraphManualEventList'; /** The time that the Paragraph was created. */ created: DateTime; - /** Arrangementer */ + /** Events */ events?: Maybe>; /** The Universally Unique IDentifier (UUID). */ id: Scalars['ID']['output']; /** The paragraphs entity language code. */ langcode: Language; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; - /** Titel */ + /** Title */ title?: Maybe; }; -/** En basal, formateret brødtekst */ +/** A basic, formatted body of text. */ export type ParagraphTextBody = ParagraphInterface & { __typename?: 'ParagraphTextBody'; - /** Brødtekst */ + /** Body */ body?: Maybe; /** The time that the Paragraph was created. */ created: DateTime; @@ -546,7 +552,7 @@ export type ParagraphTextBody = ParagraphInterface & { id: Scalars['ID']['output']; /** The paragraphs entity language code. */ langcode: Language; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; }; @@ -556,6 +562,8 @@ export type ParagraphUnion = ParagraphAccordion | ParagraphBanner | ParagraphBre /** The schema's entry-point for queries. */ export type Query = { __typename?: 'Query'; + /** DPL Configuration */ + dplConfiguration?: Maybe; /** Schema information. */ info: SchemaInformation; /** Load a NodeArticle entity by id */ @@ -603,9 +611,9 @@ export type TermBreadcrumbStructure = TermInterface & { __typename?: 'TermBreadcrumbStructure'; /** Datoen hvor termen senest blev redigeret. */ changed: DateTime; - /** Titlen, der vises over listen af refereret indhold. Vil ikke blive vist, hvis der ikke vises nogen børn. */ + /** The title that is shown above the list of referenced content. Will not be shown, if there are no children displayed. */ childrenTitle?: Maybe; - /** Indhold der linkes til */ + /** Content */ content: NodeUnion; /** Beskrivelse */ description: Text; @@ -613,17 +621,17 @@ export type TermBreadcrumbStructure = TermInterface & { id: Scalars['ID']['output']; /** Term sprogkode. */ langcode: Language; - /** Navn */ + /** Name */ name: Scalars['String']['output']; /** Denne terms overordnede termer. */ parent?: Maybe; /** Alternativ URL */ path: Scalars['String']['output']; - /** Vis en automatisk liste med indhold, som refererer til dette brødkrumme element, på denne side. */ + /** Should a list of contents that reference this breadcrumb, be shown automatically on this page? */ showChildren?: Maybe; /** If this is checked, the children teasers will be expanded with possible subtitle descriptions. */ showChildrenSubtitles?: Maybe; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; /** Vægten af denne term i forhold til andre termer. */ weight: Scalars['Int']['output']; @@ -640,13 +648,13 @@ export type TermCategories = TermInterface & { id: Scalars['ID']['output']; /** Term sprogkode. */ langcode: Language; - /** Navn */ + /** Name */ name: Scalars['String']['output']; /** Denne terms overordnede termer. */ parent?: Maybe; /** Alternativ URL */ path: Scalars['String']['output']; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; /** Vægten af denne term i forhold til andre termer. */ weight: Scalars['Int']['output']; @@ -662,19 +670,19 @@ export type TermInterface = { id: Scalars['ID']['output']; /** Term sprogkode. */ langcode: Language; - /** Navn */ + /** Name */ name: Scalars['String']['output']; /** Denne terms overordnede termer. */ parent?: Maybe; /** Alternativ URL */ path: Scalars['String']['output']; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; /** Vægten af denne term i forhold til andre termer. */ weight: Scalars['Int']['output']; }; -/** This is for adding types of opening hours, e.g. "open" or "Telephone time" */ +/** Kategorier af åbningstider, f.eks. "Åbent" eller "Telefontid" */ export type TermOpeningHoursCategories = TermInterface & { __typename?: 'TermOpeningHoursCategories'; /** Datoen hvor termen senest blev redigeret. */ @@ -685,13 +693,13 @@ export type TermOpeningHoursCategories = TermInterface & { id: Scalars['ID']['output']; /** Term sprogkode. */ langcode: Language; - /** Navn */ + /** Name */ name: Scalars['String']['output']; /** Denne terms overordnede termer. */ parent?: Maybe; /** Alternativ URL */ path: Scalars['String']['output']; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; /** Vægten af denne term i forhold til andre termer. */ weight: Scalars['Int']['output']; @@ -708,13 +716,13 @@ export type TermTags = TermInterface & { id: Scalars['ID']['output']; /** Term sprogkode. */ langcode: Language; - /** Navn */ + /** Name */ name: Scalars['String']['output']; /** Denne terms overordnede termer. */ parent?: Maybe; /** Alternativ URL */ path: Scalars['String']['output']; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; /** Vægten af denne term i forhold til andre termer. */ weight: Scalars['Int']['output']; @@ -736,13 +744,13 @@ export type TermWebformEmailCategories = TermInterface & { id: Scalars['ID']['output']; /** Term sprogkode. */ langcode: Language; - /** Navn */ + /** Name */ name: Scalars['String']['output']; /** Denne terms overordnede termer. */ parent?: Maybe; /** Alternativ URL */ path: Scalars['String']['output']; - /** Publiceret */ + /** Published */ status: Scalars['Boolean']['output']; /** Vægten af denne term i forhold til andre termer. */ weight: Scalars['Int']['output']; @@ -783,6 +791,15 @@ export type Translation = { title?: Maybe; }; +/** List of DPL-Go Unilogin configuration. */ +export type UniloginConfiguration = { + __typename?: 'UniloginConfiguration'; + unilogin_api_client_id?: Maybe; + unilogin_api_client_secret?: Maybe; + unilogin_api_url?: Maybe; + unilogin_api_wellknown_url?: Maybe; +}; + /** * Unsupported entity or field type in the schema. * This entity may not have been enabled in the schema yet and is being referenced via entity reference. @@ -800,6 +817,11 @@ export type GetArticleQueryVariables = Exact<{ export type GetArticleQuery = { __typename?: 'Query', nodeArticle?: { __typename?: 'NodeArticle', title: string, subtitle?: string | null, paragraphs?: Array<{ __typename: 'ParagraphAccordion' } | { __typename: 'ParagraphBanner' } | { __typename: 'ParagraphBreadcrumbChildren' } | { __typename: 'ParagraphCardGridAutomatic' } | { __typename: 'ParagraphCardGridManual' } | { __typename: 'ParagraphContentSlider' } | { __typename: 'ParagraphContentSliderAutomatic' } | { __typename: 'ParagraphFilteredEventList' } | { __typename: 'ParagraphManualEventList' } | { __typename: 'ParagraphTextBody', body?: { __typename?: 'Text', value?: string | null } | null }> | null } | null }; +export type GetUniLoginConfigurationQueryVariables = Exact<{ [key: string]: never; }>; + + +export type GetUniLoginConfigurationQuery = { __typename?: 'Query', dplConfiguration?: { __typename?: 'DplConfiguration', unilogin?: { __typename?: 'UniloginConfiguration', unilogin_api_url?: string | null, unilogin_api_wellknown_url?: string | null, unilogin_api_client_id?: string | null, unilogin_api_client_secret?: string | null } | null } | null }; + export const GetArticleDocument = ` @@ -857,3 +879,55 @@ useSuspenseGetArticleQuery.getKey = (variables: GetArticleQueryVariables) => ['g useGetArticleQuery.fetcher = (variables: GetArticleQueryVariables) => fetcher(GetArticleDocument, variables); + +export const GetUniLoginConfigurationDocument = ` + query getUniLoginConfiguration { + dplConfiguration { + unilogin { + unilogin_api_url + unilogin_api_wellknown_url + unilogin_api_client_id + unilogin_api_client_secret + } + } +} + `; + +export const useGetUniLoginConfigurationQuery = < + TData = GetUniLoginConfigurationQuery, + TError = unknown + >( + variables?: GetUniLoginConfigurationQueryVariables, + options?: Omit, 'queryKey'> & { queryKey?: UseQueryOptions['queryKey'] } + ) => { + + return useQuery( + { + queryKey: variables === undefined ? ['getUniLoginConfiguration'] : ['getUniLoginConfiguration', variables], + queryFn: fetcher(GetUniLoginConfigurationDocument, variables), + ...options + } + )}; + +useGetUniLoginConfigurationQuery.getKey = (variables?: GetUniLoginConfigurationQueryVariables) => variables === undefined ? ['getUniLoginConfiguration'] : ['getUniLoginConfiguration', variables]; + +export const useSuspenseGetUniLoginConfigurationQuery = < + TData = GetUniLoginConfigurationQuery, + TError = unknown + >( + variables?: GetUniLoginConfigurationQueryVariables, + options?: Omit, 'queryKey'> & { queryKey?: UseSuspenseQueryOptions['queryKey'] } + ) => { + + return useSuspenseQuery( + { + queryKey: variables === undefined ? ['getUniLoginConfigurationSuspense'] : ['getUniLoginConfigurationSuspense', variables], + queryFn: fetcher(GetUniLoginConfigurationDocument, variables), + ...options + } + )}; + +useSuspenseGetUniLoginConfigurationQuery.getKey = (variables?: GetUniLoginConfigurationQueryVariables) => variables === undefined ? ['getUniLoginConfigurationSuspense'] : ['getUniLoginConfigurationSuspense', variables]; + + +useGetUniLoginConfigurationQuery.fetcher = (variables?: GetUniLoginConfigurationQueryVariables) => fetcher(GetUniLoginConfigurationDocument, variables); diff --git a/lib/graphql/generated/fbi/graphql.tsx b/lib/graphql/generated/fbi/graphql.tsx index 3b67c0a5..d04e420f 100644 --- a/lib/graphql/generated/fbi/graphql.tsx +++ b/lib/graphql/generated/fbi/graphql.tsx @@ -20,7 +20,7 @@ export type Scalars = { export type AccessType = { __typename?: 'AccessType'; - code: AccessTypeCodeEnum; + code: AccessTypeCodeEnum | '%future added value'; display: Scalars['String']['output']; }; @@ -41,9 +41,9 @@ export type AccessUrl = { /** The origin, e.g. "DBC Webarkiv" */ origin: Scalars['String']['output']; /** Status from linkcheck */ - status: LinkStatusEnum; + status: LinkStatusEnum | '%future added value'; /** The type of content that can be found at this URL */ - type?: Maybe; + type?: Maybe; /** The url where manifestation is located */ url: Scalars['String']['output']; }; @@ -95,7 +95,7 @@ export type CatalogueCodes = { export type ChildOrAdult = { __typename?: 'ChildOrAdult'; - code: ChildOrAdultCodeEnum; + code: ChildOrAdultCodeEnum | '%future added value'; display: Scalars['String']['output']; }; @@ -113,7 +113,7 @@ export type Classification = { /** The dk5Heading for the classification (DK5 only) */ dk5Heading?: Maybe; /** For DK5 only. The DK5 entry type: main entry, national entry, or additional entry */ - entryType?: Maybe; + entryType?: Maybe; /** Name of the classification system */ system: Scalars['String']['output']; }; @@ -177,7 +177,7 @@ export enum ComplexSearchFacetsEnum { /** The facets to ask for */ export type ComplexSearchFacetsInput = { facetLimit: Scalars['Int']['input']; - facets?: InputMaybe>; + facets?: InputMaybe>; }; /** Search Filters */ @@ -190,6 +190,8 @@ export type ComplexSearchFiltersInput = { branchId?: InputMaybe>; /** Overall location in library (eg. Voksne). */ department?: InputMaybe>; + /** Date of first accession */ + firstAccessionDate?: InputMaybe; /** Id of publishing issue. */ issueId?: InputMaybe>; /** Local id of the item. */ @@ -197,7 +199,7 @@ export type ComplexSearchFiltersInput = { /** Where is the book physically located (eg. skønlitteratur). */ location?: InputMaybe>; /** Onloan or OnShelf. */ - status?: InputMaybe>; + status?: InputMaybe>; /** More specific location (eg. Fantasy). */ sublocation?: InputMaybe>; }; @@ -280,7 +282,7 @@ export type CopyRequestInput = { export type CopyRequestResponse = { __typename?: 'CopyRequestResponse'; - status: CopyRequestStatusEnum; + status: CopyRequestStatusEnum | '%future added value'; }; export enum CopyRequestStatusEnum { @@ -319,7 +321,7 @@ export type Corporation = CreatorInterface & SubjectInterface & { roles: Array; /** Sub corporation or conference/meeting */ sub?: Maybe; - type: SubjectTypeEnum; + type: SubjectTypeEnum | '%future added value'; /** Year of the conference */ year?: Maybe; }; @@ -454,7 +456,7 @@ export type FacetResult = { /** The name of the facet. */ name: Scalars['String']['output']; /** The enum type of the facet. */ - type: FacetFieldEnum; + type: FacetFieldEnum | '%future added value'; /** The values of thie facet result */ values: Array; }; @@ -479,7 +481,7 @@ export type FacetValue = { export type FictionNonfiction = { __typename?: 'FictionNonfiction'; /** Binary code fiction/nonfiction used for filtering */ - code: FictionNonfictionCodeEnum; + code: FictionNonfictionCodeEnum | '%future added value'; /** Displayable overall category/genre. In Danish skønlitteratur/faglitteratur for literature, fiktion/nonfiktion for other types. */ display: Scalars['String']['output']; }; @@ -493,7 +495,7 @@ export enum FictionNonfictionCodeEnum { export type GeneralMaterialType = { __typename?: 'GeneralMaterialType'; /** code for materialType # @TODO - is this a finite list ?? - and where to get it */ - code: GeneralMaterialTypeCodeEnum; + code: GeneralMaterialTypeCodeEnum | '%future added value'; /** Ths string to display */ display: Scalars['String']['output']; }; @@ -554,7 +556,7 @@ export type HostPublication = { export type Identifier = { __typename?: 'Identifier'; /** The type of identifier */ - type: IdentifierTypeEnum; + type: IdentifierTypeEnum | '%future added value'; /** The actual identifier */ value: Scalars['String']['output']; }; @@ -605,7 +607,7 @@ export type InfomediaResponse = { __typename?: 'InfomediaResponse'; article?: Maybe; /** Infomedia error */ - error?: Maybe; + error?: Maybe; }; export type InfomediaService = { @@ -703,7 +705,7 @@ export type LinkCheckResponse = { __typename?: 'LinkCheckResponse'; brokenSince?: Maybe; lastCheckedAt?: Maybe; - status: LinkCheckStatusEnum; + status: LinkCheckStatusEnum | '%future added value'; url: Scalars['String']['output']; }; @@ -817,7 +819,7 @@ export type Manifestation = { /** Information about on which volume this manifestation is in multi volume work */ volume?: Maybe; /** Worktypes for this manifestations work */ - workTypes: Array; + workTypes: Array; /** The year this manifestation was originally published or produced */ workYear?: Maybe; }; @@ -854,7 +856,7 @@ export type ManifestationParts = { /** The creator and title etc of the individual parts */ parts: Array; /** The type of manifestation parts, is this music tracks, book parts etc. */ - type: ManifestationPartTypeEnum; + type: ManifestationPartTypeEnum | '%future added value'; }; export type ManifestationReview = { @@ -919,12 +921,12 @@ export type Mood = SubjectInterface & { display: Scalars['String']['output']; language?: Maybe; local?: Maybe; - type: SubjectTypeEnum; + type: SubjectTypeEnum | '%future added value'; }; export type MoodKidsRecommendFiltersInput = { difficulty?: InputMaybe>; - fictionNonfiction?: InputMaybe; + fictionNonfiction?: InputMaybe; illustrationsLevel?: InputMaybe>; length?: InputMaybe>; realisticVsFictional?: InputMaybe>; @@ -1054,7 +1056,7 @@ export type MoodSuggestItem = { /** Suggestion */ term: Scalars['String']['output']; /** The type of suggestion title/creator/tag */ - type: MoodSuggestEnum; + type: MoodSuggestEnum | '%future added value'; /** A work associated with the suggestion */ work?: Maybe; }; @@ -1083,7 +1085,7 @@ export type NarrativeTechnique = SubjectInterface & { display: Scalars['String']['output']; language?: Maybe; local?: Maybe; - type: SubjectTypeEnum; + type: SubjectTypeEnum | '%future added value'; }; export type Note = { @@ -1093,7 +1095,7 @@ export type Note = { /** Heading before note */ heading?: Maybe; /** The type of note - e.g. note about language, genre etc, NOT_SPECIFIED if not known. */ - type: NoteTypeEnum; + type: NoteTypeEnum | '%future added value'; }; export enum NoteTypeEnum { @@ -1155,7 +1157,7 @@ export type Person = CreatorInterface & SubjectInterface & { roles: Array; /** A roman numeral added to the person, like Christian IV */ romanNumeral?: Maybe; - type: SubjectTypeEnum; + type: SubjectTypeEnum | '%future added value'; }; export type PhysicalUnitDescription = { @@ -1435,7 +1437,7 @@ export type ReviewElement = { heading?: Maybe; /** Manifestations that can be used to generate and insert links into 'contentSubsitute'. */ manifestations?: Maybe>>; - type?: Maybe; + type?: Maybe; }; export enum ReviewElementTypeEnum { @@ -1458,7 +1460,7 @@ export type Role = { export type SchoolUse = { __typename?: 'SchoolUse'; - code: SchoolUseCodeEnum; + code: SchoolUseCodeEnum | '%future added value'; display: Scalars['String']['output']; }; @@ -1489,7 +1491,7 @@ export type SearchFiltersInput = { mainLanguages?: InputMaybe>; materialTypesGeneral?: InputMaybe>; materialTypesSpecific?: InputMaybe>; - status?: InputMaybe>; + status?: InputMaybe>; subjects?: InputMaybe>; sublocation?: InputMaybe>; workTypes?: InputMaybe>; @@ -1607,7 +1609,7 @@ export type Setting = SubjectInterface & { display: Scalars['String']['output']; language?: Maybe; local?: Maybe; - type: SubjectTypeEnum; + type: SubjectTypeEnum | '%future added value'; }; export type Shelfmark = { @@ -1620,7 +1622,7 @@ export type Shelfmark = { export type SortInput = { index: Scalars['String']['input']; - order: SortOrderEnum; + order: SortOrderEnum | '%future added value'; }; export enum SortOrderEnum { @@ -1650,7 +1652,7 @@ export type SubjectInterface = { language?: Maybe; local?: Maybe; /** The type of subject - 'location', 'time period' etc., 'topic' if not specific kind of subject term */ - type: SubjectTypeEnum; + type: SubjectTypeEnum | '%future added value'; }; export type SubjectText = SubjectInterface & { @@ -1658,7 +1660,7 @@ export type SubjectText = SubjectInterface & { display: Scalars['String']['output']; language?: Maybe; local?: Maybe; - type: SubjectTypeEnum; + type: SubjectTypeEnum | '%future added value'; }; export enum SubjectTypeEnum { @@ -1697,7 +1699,7 @@ export type SubjectWithRating = SubjectInterface & { local?: Maybe; /** Expressed as integer on a scale from 1 to 5 */ rating?: Maybe; - type: SubjectTypeEnum; + type: SubjectTypeEnum | '%future added value'; }; export type SuggestResponse = { @@ -1710,7 +1712,7 @@ export type Suggestion = { /** The suggested term which can be searched for */ term: Scalars['String']['output']; /** The type of suggestion: creator, subject or title */ - type: SuggestionTypeEnum; + type: SuggestionTypeEnum | '%future added value'; /** A work related to the term */ work?: Maybe; }; @@ -1735,7 +1737,7 @@ export type TimePeriod = SubjectInterface & { language?: Maybe; local?: Maybe; period: Range; - type: SubjectTypeEnum; + type: SubjectTypeEnum | '%future added value'; }; export type Translation = { @@ -1809,7 +1811,7 @@ export type Universe = { /** An id that identifies a universe. */ universeId?: Maybe; /** work types that are in this universe */ - workTypes: Array; + workTypes: Array; /** All works within the universe but not in any series */ works: Array; }; @@ -1875,7 +1877,7 @@ export type Work = { /** Unique identification of the work based on work-presentation id e.g work-of:870970-basis:54029519 */ workId: Scalars['String']['output']; /** Worktypes for this work - 'none' replaced by 'other' */ - workTypes: Array; + workTypes: Array; /** The year this work was originally published or produced */ workYear?: Maybe; }; diff --git a/lib/graphql/queries/uniLogin.dpl-cms.graphql b/lib/graphql/queries/uniLogin.dpl-cms.graphql new file mode 100644 index 00000000..2e14c531 --- /dev/null +++ b/lib/graphql/queries/uniLogin.dpl-cms.graphql @@ -0,0 +1,10 @@ +query getUniLoginConfiguration { + dplConfiguration { + unilogin { + unilogin_api_url + unilogin_api_wellknown_url + unilogin_api_client_id + unilogin_api_client_secret + } + } +} diff --git a/lib/session/oauth/uniloginClient.ts b/lib/session/oauth/uniloginClient.ts index 0e78bc79..a4330b09 100644 --- a/lib/session/oauth/uniloginClient.ts +++ b/lib/session/oauth/uniloginClient.ts @@ -2,14 +2,18 @@ import { Issuer } from "openid-client" import goConfig from "@/lib/config/config" -const appUrl = goConfig("app.url") +const appUrl = goConfig("app.url") as string +const uniloginApiUrl = goConfig("service.unilogin.api.url") as string +const uniloginWellKnownUrl = goConfig("service.unilogin.wellknown.url") as string +const clientId = goConfig("service.unilogin.client-id") as string +const clientSecret = goConfig("service.unilogin.client-secret") as string export const uniloginClientConfig = { - wellKnownUrl: process.env.UNILOGIN_WELKNOWN_URL, - url: process.env.UNILOGIN_API_URL, + wellKnownUrl: uniloginWellKnownUrl, + url: uniloginApiUrl, audience: process.env.UNILOGIN_API_URL, - client_id: process.env.UNILOGIN_CLIENT_ID, - client_secret: process.env.UNILOGIN_CLIENT_SECRET, + client_id: clientId, + client_secret: clientSecret, scope: "openid", redirect_uri: `${appUrl}/auth/callback/unilogin`, post_logout_redirect_uri: `${appUrl}`, @@ -19,7 +23,7 @@ export const uniloginClientConfig = { } export async function getUniloginClient() { - const UniloginIssuer = await Issuer.discover(uniloginClientConfig.wellKnownUrl!) + const UniloginIssuer = await Issuer.discover(uniloginClientConfig.wellKnownUrl) const client = new UniloginIssuer.Client({ client_id: uniloginClientConfig.client_id!, client_secret: uniloginClientConfig.client_secret!, diff --git a/package.json b/package.json index dfe20957..967c26a3 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "format:check": "prettier --check .", "format:write": "prettier --write .", "test:unit": "vitest", - "codegen:graphql": "graphql-codegen --config codegen.ts", + "codegen:graphql": "graphql-codegen --require tsconfig-paths/register --config codegen.ts", "codegen:all-rest-services": "orval", "codegen:covers": "rm -rf src/lib/rest/cover-service-api/generated/model/*.* && orval --project coverService", "codegen:publizon": "rm -rf lib/rest/publizon-api/generated/model/*.* && orval --project publizonAdapter", diff --git a/tsconfig.json b/tsconfig.json index 3ce184b6..15417281 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,6 +23,7 @@ "schema": "graphql/generated/dpl-cms/graphql.schema.json" } ], + "baseUrl": ".", "paths": { "@/*": ["./*"] } @@ -32,7 +33,6 @@ "node_modules", "lib/rest/cover-service-api/model", "lib/rest/cover-service-api/cover-service.ts", - "lib/graphql/generated/fbi/*.tsx", - "__tests__" + "lib/graphql/generated/fbi/*.tsx" ] } From b6dbb97c91f9a66bc2cdeeff40af5150b2752633 Mon Sep 17 00:00:00 2001 From: thomasgross Date: Fri, 15 Nov 2024 14:24:29 +0100 Subject: [PATCH 02/23] feat: add type checking and CI check scripts to package.json --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index 967c26a3..fab46323 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ "format:check": "prettier --check .", "format:write": "prettier --write .", "test:unit": "vitest", + "typecheck": "tsc --noEmit", + "ci-check": "yarn typecheck && yarn lint && yarn test:unit && format:check", "codegen:graphql": "graphql-codegen --require tsconfig-paths/register --config codegen.ts", "codegen:all-rest-services": "orval", "codegen:covers": "rm -rf src/lib/rest/cover-service-api/generated/model/*.* && orval --project coverService", From c52b5c66b7f3a501b696ad0736a2dcd453461ddb Mon Sep 17 00:00:00 2001 From: thomasgross Date: Fri, 15 Nov 2024 14:24:42 +0100 Subject: [PATCH 03/23] fix: correct ci-check script to include yarn format:check --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fab46323..7f85f177 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "format:write": "prettier --write .", "test:unit": "vitest", "typecheck": "tsc --noEmit", - "ci-check": "yarn typecheck && yarn lint && yarn test:unit && format:check", + "ci-check": "yarn typecheck && yarn lint && yarn test:unit && yarn format:check", "codegen:graphql": "graphql-codegen --require tsconfig-paths/register --config codegen.ts", "codegen:all-rest-services": "orval", "codegen:covers": "rm -rf src/lib/rest/cover-service-api/generated/model/*.* && orval --project coverService", From 10e51faa4b1fab637dbf5a280266d092eaf4fb2c Mon Sep 17 00:00:00 2001 From: thomasgross Date: Fri, 15 Nov 2024 14:24:59 +0100 Subject: [PATCH 04/23] fix: add TypeScript ignore comments for mocked configurations in search tests --- __tests__/search.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/__tests__/search.test.ts b/__tests__/search.test.ts index b6d52d41..bec98730 100644 --- a/__tests__/search.test.ts +++ b/__tests__/search.test.ts @@ -32,6 +32,7 @@ export const facets = { } as const beforeEach(() => { + // @ts-ignore ;(goConfig as jest.Mock).mockImplementation((key: string) => { if (key === "search.item.limit") { return 9 // Mocked return value for "search.item.limit" @@ -49,6 +50,7 @@ beforeEach(() => { describe("Facet functionality", () => { it("getMachineNames should return all the facet machine names", () => { + // @ts-ignore goConfig.mockReturnValue(facets) const machineNames = getFacetMachineNames() expect(machineNames).toStrictEqual([ @@ -109,6 +111,7 @@ describe("Facet functionality", () => { }) it("getFacetTranslation should give a translated facet when given a facet machine name", () => { + // @ts-ignore const translation = getFacetTranslation("LIX") expect(translation).toBe("Lix") }) From 006f1e5f40e7c41c8f3c89f17e92e1bf5b5e75e8 Mon Sep 17 00:00:00 2001 From: thomasgross Date: Mon, 18 Nov 2024 14:45:48 +0100 Subject: [PATCH 05/23] feat: implement ConfigProvider and integrate UniLogin configuration handling --- .env.example | 4 +- .env.test | 11 ++--- app/layout.tsx | 36 ++++++++++----- app/page.tsx | 9 +++- codegen.ts | 15 ++----- lib/config/resolvers/service.dpl-cms.ts | 5 +++ lib/config/resolvers/service.unilogin.ts | 54 +++++++++-------------- lib/graphql/fetchers/dpl-cms.fetcher.ts | 24 ++++++++++ lib/graphql/generated/dpl-cms/graphql.tsx | 25 ++--------- lib/graphql/generated/fbi/graphql.tsx | 2 + lib/providers/ConfigProvider.tsx | 18 ++++++++ package.json | 2 +- store/config.store.ts | 27 ++++++++++++ 13 files changed, 146 insertions(+), 86 deletions(-) create mode 100644 lib/graphql/fetchers/dpl-cms.fetcher.ts create mode 100644 lib/providers/ConfigProvider.tsx create mode 100644 store/config.store.ts diff --git a/.env.example b/.env.example index 93a77cb7..9715dfbf 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,9 @@ NEXT_PUBLIC_GRAPHQL_SCHEMA_ENDPOINT_DPL_CMS=http://dapple-cms.docker/graphql NEXT_PUBLIC_GRAPHQL_SCHEMA_ENDPOINT_FBI=https://temp.fbi-api.dbc.dk/ereolgo/graphql // WILL BE REPLACED WITH DYNAMIC TOKEN NEXT_PUBLIC_LIBRARY_TOKEN=XXX +NEXT_PUBLIC_APP_URL=http://localhost:3000 UNILOGIN_BASIC_AUTH_HEADER=XXX UNILOGIN_SESSION_SECRET=XXX -NEXT_PUBLIC_APP_URL=http://localhost:3000 + + diff --git a/.env.test b/.env.test index 0a6ba101..b99fb3af 100644 --- a/.env.test +++ b/.env.test @@ -2,11 +2,12 @@ NEXT_PUBLIC_GRAPHQL_SCHEMA_ENDPOINT_DPL_CMS=http://dapple-cms.docker/graphql NEXT_PUBLIC_GRAPHQL_SCHEMA_ENDPOINT_FBI=https://temp.fbi-api.dbc.dk/ereolgo/graphql // WILL BE REPLACED WITH DYNAMIC TOKEN NEXT_PUBLIC_LIBRARY_TOKEN=XXX +NEXT_PUBLIC_APP_URL=https://hellboy.the-movie.com UNILOGIN_SESSION_SECRET=XXX -UNILOGIN_API_URL=https://et-broker.unilogin.dk -UNILOGIN_WELKNOWN_URL=https://et-broker.unilogin.dk/auth/realms/broker/.well-known/openid-configuration -UNILOGIN_CLIENT_ID=XXX -UNILOGIN_CLIENT_SECRET=XXXX -NEXT_PUBLIC_APP_URL=https://hellboy.the-movie.com +UNILOGIN_API_URL_TEST=https://et-broker.unilogin.dk +UNILOGIN_WELLKNOWN_URL_TEST=https://et-broker.unilogin.dk/auth/realms/broker/.well-known/openid-configuration +UNILOGIN_CLIENT_ID_TEST=XXX +UNILOGIN_CLIENT_SECRET_TEST=XXXX + LAGOON_AUTOGENERATED_ROUTES=https://nginx.acme.com,https://node.acme.com,https://varnish.acme.com diff --git a/app/layout.tsx b/app/layout.tsx index ae26dd42..298bf43f 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,3 +1,4 @@ +import { QueryClient } from "@tanstack/react-query" import type { Metadata } from "next" import localFont from "next/font/local" @@ -5,8 +6,12 @@ import Footer from "@/components/global/footer/Footer" import GridHelper from "@/components/global/gridHelper/GridHelper" import Header from "@/components/global/header/Header" import Theme from "@/components/global/theme/Theme" -import { initUniLoginConfiguration } from "@/lib/config/resolvers/service.unilogin" import getQueryClient from "@/lib/getQueryClient" +import { + GetUniLoginConfigurationQuery, + useGetUniLoginConfigurationQuery, +} from "@/lib/graphql/generated/dpl-cms/graphql" +import ConfigProvider from "@/lib/providers/ConfigProvider" import ReactQueryProvider from "@/lib/providers/ReactQueryProvider" import "@/styles/globals.css" @@ -37,27 +42,38 @@ const GTFlexa = localFont({ display: "swap", }) -export default function RootLayout({ +const getConfiguration = async (queryClient: QueryClient) => { + const data = await queryClient.fetchQuery({ + queryKey: useGetUniLoginConfigurationQuery.getKey(), + queryFn: useGetUniLoginConfigurationQuery.fetcher(), + }) + + return data +} + +export default async function RootLayout({ children, }: Readonly<{ children: React.ReactNode }>) { // This is a workaround to ensure that the uniloginConfiguration is loaded before the app is rendered const queryClient = getQueryClient() - initUniLoginConfiguration(queryClient) + const configuration = await getConfiguration(queryClient) return ( - -
-
- {children} -
-