diff --git a/.env.example b/.env.example index faf22f79..5e8b9cff 100644 --- a/.env.example +++ b/.env.example @@ -1,12 +1,12 @@ NEXT_PUBLIC_GRAPHQL_SCHEMA_ENDPOINT_DPL_CMS=http://dapple-cms.docker/graphql +NEXT_PUBLIC_GRAPHQL_BASIC_TOKEN_DPL_CMS=Z3JhcGhxbF9jb25zdW1lcjp0ZXN0 + 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 -UNILOGIN_SESSION_SECRET=XXX UNILOGIN_API_URL=https://et-broker.unilogin.dk -UNILOGIN_WELLKNOWN_URL=https://et-broker.unilogin.dk/auth/realms/broker/.well-known/openid-configuration -UNILOGIN_CLIENT_ID=XXX -UNILOGIN_CLIENT_SECRET=XXXX +UNILOGIN_CLIENT_ID=https://stg.ereolengo.itkdev.dk/ +UNILOGIN_CLIENT_SECRET=XXX +UNILOGIN_SESSION_SECRET=XXX NEXT_PUBLIC_APP_URL=https://localhost:3000 diff --git a/.env.test b/.env.test index 30e04de0..b7772df6 100644 --- a/.env.test +++ b/.env.test @@ -1,12 +1,12 @@ NEXT_PUBLIC_GRAPHQL_SCHEMA_ENDPOINT_DPL_CMS=http://dapple-cms.docker/graphql +NEXT_PUBLIC_GRAPHQL_BASIC_TOKEN_DPL_CMS=Z3JhcGhxbF9jb25zdW1lcjp0ZXN0 + 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 -UNILOGIN_SESSION_SECRET=XXX UNILOGIN_API_URL=https://et-broker.unilogin.dk -UNILOGIN_WELLKNOWN_URL=https://et-broker.unilogin.dk/auth/realms/broker/.well-known/openid-configuration -UNILOGIN_CLIENT_ID=XXX -UNILOGIN_CLIENT_SECRET=XXXX +UNILOGIN_CLIENT_ID=https://stg.ereolengo.itkdev.dk/ +UNILOGIN_CLIENT_SECRET=XXX +UNILOGIN_SESSION_SECRET=XXX + NEXT_PUBLIC_APP_URL=https://hellboy.the-movie.com -LAGOON_AUTOGENERATED_ROUTES=https://nginx.acme.com,https://node.acme.com,https://varnish.acme.com diff --git a/__tests__/config.test.ts b/__tests__/config.test.ts index c9e9617b..67b228fb 100644 --- a/__tests__/config.test.ts +++ b/__tests__/config.test.ts @@ -1,14 +1,17 @@ +import { describe } from "node:test" import { expect, test } from "vitest" -import goConfig from "@/lib/config/config" import MissingConfigurationError from "@/lib/config/errors/MissingConfigurationError" +import goConfig from "@/lib/config/goConfig" -test("That an error is thrown if we ask for unknown config", async () => { - // @ts-ignore - expect(() => goConfig("unknown.thingy")).toThrowError(MissingConfigurationError) -}) +describe("Config test suite", () => { + test("That an error is thrown if we ask for unknown config", async () => { + // @ts-ignore + expect(() => goConfig("unknown.thingy")).toThrowError(MissingConfigurationError) + }) -test("That the env variable NEXT_PUBLIC_APP_URL has precedence over LAGOON_AUTOGENERATED_ROUTES", async () => { - const appUrl = goConfig("app.url") - expect(appUrl).toBe("https://hellboy.the-movie.com") + test("That the env variable NEXT_PUBLIC_APP_URL has precedence over LAGOON_AUTOGENERATED_ROUTES", async () => { + const appUrl = goConfig("app.url") + expect(appUrl).toBe("https://hellboy.the-movie.com") + }) }) diff --git a/__tests__/search.test.ts b/__tests__/search.test.ts index bec98730..5ab20728 100644 --- a/__tests__/search.test.ts +++ b/__tests__/search.test.ts @@ -6,10 +6,10 @@ import { getSearchQueryArguments, } from "@/components/pages/searchPageLayout/helper" import { getFacetMachineNames, getFacetTranslation } from "@/components/shared/searchFilters/helper" -import goConfig from "@/lib/config/config" +import goConfig from "@/lib/config/goConfig" import { FacetFieldEnum } from "@/lib/graphql/generated/fbi/graphql" -vi.mock(import("@/lib/config/config"), async importOriginal => { +vi.mock(import("@/lib/config/goConfig"), async importOriginal => { const actual = await importOriginal() return { ...actual, diff --git a/app/auth/callback/unilogin/route.ts b/app/auth/callback/unilogin/route.ts index 50bcda2c..cbd14f91 100644 --- a/app/auth/callback/unilogin/route.ts +++ b/app/auth/callback/unilogin/route.ts @@ -2,7 +2,7 @@ import { sealData } from "iron-session" import { NextRequest, NextResponse } from "next/server" import * as client from "openid-client" -import goConfig from "@/lib/config/config" +import goConfig from "@/lib/config/goConfig" import { getUniloginClientConfig } from "@/lib/session/oauth/uniloginClient" import { getSession, sessionOptions, setTokensOnSession } from "@/lib/session/session" import { TTokenSet } from "@/lib/types/session" diff --git a/app/auth/login/unilogin/route.ts b/app/auth/login/unilogin/route.ts index d54d663f..c5ba7671 100644 --- a/app/auth/login/unilogin/route.ts +++ b/app/auth/login/unilogin/route.ts @@ -1,6 +1,6 @@ import * as client from "openid-client" -import goConfig from "@/lib/config/config" +import goConfig from "@/lib/config/goConfig" import { getUniloginClientConfig } from "@/lib/session/oauth/uniloginClient" import { getSession } from "@/lib/session/session" diff --git a/app/auth/logout/route.ts b/app/auth/logout/route.ts index 4c97add8..e49f3fc3 100644 --- a/app/auth/logout/route.ts +++ b/app/auth/logout/route.ts @@ -1,14 +1,14 @@ import { cookies } from "next/headers" import * as client from "openid-client" -import goConfig from "@/lib/config/config" +import goConfig from "@/lib/config/goConfig" import { getUniloginClientConfig } from "@/lib/session/oauth/uniloginClient" import { getSession } from "@/lib/session/session" export async function GET() { const session = await getSession() const config = await getUniloginClientConfig() - const appUrl = new URL(goConfig("app.url")) + const appUrl = new URL(String(goConfig("app.url"))) session.destroy() @@ -16,7 +16,7 @@ export async function GET() { const id_token = cookies().get("go-session:id_token")?.value // TODO: Is this where we want to redirect to if id token cannot be resolved? if (!id_token) { - return Response.redirect(goConfig("app.url")) + return Response.redirect(appUrl) } const endSessionEndpoint = config.serverMetadata().end_session_endpoint @@ -31,5 +31,5 @@ export async function GET() { // End session in Unilogin SSO. await fetch(endSessionUrl) - return Response.redirect(`${appUrl}?reload-session=true`) + return Response.redirect(`${appUrl.toString()}?reload-session=true`) } diff --git a/app/auth/token/refresh/route.ts b/app/auth/token/refresh/route.ts index 953ff0b2..26d57980 100644 --- a/app/auth/token/refresh/route.ts +++ b/app/auth/token/refresh/route.ts @@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from "next/server" import * as client from "openid-client" import { z } from "zod" -import goConfig from "@/lib/config/config" +import goConfig from "@/lib/config/goConfig" import { getUniloginClientConfig } from "@/lib/session/oauth/uniloginClient" import { getSession, setTokensOnSession } from "@/lib/session/session" import { TTokenSet } from "@/lib/types/session" @@ -14,7 +14,7 @@ const sessionTokenSchema = z.object({ }) export async function GET(request: NextRequest, response: NextResponse) { - const appUrl = goConfig("app.url") + const appUrl = String(goConfig("app.url")) const config = await getUniloginClientConfig() // TODO: Fix refresh token flow with new openid-client. diff --git a/app/page.tsx b/app/page.tsx index 3ae0fd9c..5ee46a9c 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,11 +1,15 @@ import Image from "next/image" +import goConfig from "@/lib/config/goConfig" + +const wellknownUrl = goConfig("service.unilogin.wellknown.url") export default async function Home() { return (
+
Wellknown url: {wellknownUrl}
("service.fbi.graphql.endpoint")]: { + [String(goConfig("service.fbi.graphql.endpoint"))]: { headers: { Authorization: `Bearer ${goConfig("token.adgangsplatformen.library")}`, }, @@ -22,8 +56,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/components/pages/searchPageLayout/helper.ts b/components/pages/searchPageLayout/helper.ts index ca9a4937..e1dd2da4 100644 --- a/components/pages/searchPageLayout/helper.ts +++ b/components/pages/searchPageLayout/helper.ts @@ -3,8 +3,7 @@ import { useSelector } from "@xstate/react" import { ReadonlyURLSearchParams } from "next/navigation" import { getFacetMachineNames } from "@/components/shared/searchFilters/helper" -import goConfig from "@/lib/config/config" -import { TConfigSearchFacets } from "@/lib/config/resolvers/search" +import goConfig from "@/lib/config/goConfig" import { SearchFiltersInput, SearchWithPaginationQuery } from "@/lib/graphql/generated/fbi/graphql" import { TFilters } from "@/lib/machines/search/types" import useSearchMachineActor from "@/lib/machines/search/useSearchMachineActor" @@ -18,29 +17,29 @@ export const getSearchQueryArguments = ({ currentPage: number facetFilters: SearchFiltersInput }) => { - const limit = goConfig("search.item.limit") + const limit = goConfig("search.item.limit") return { q: { all: q }, offset: currentPage * limit, limit: limit, filters: { - branchId: goConfig<`${number}`[]>("search.branch.ids"), + branchId: goConfig("search.branch.ids"), ...facetFilters, }, } } export const getFacetsForSearchRequest = (searchParams: ReadonlyURLSearchParams) => { - const facets = goConfig("search.facets") + const facets = goConfig("search.facets") const facetsMachineNames = getFacetMachineNames() return facetsMachineNames.reduce( (acc: TFilters, machineName) => { - const values = searchParams.getAll(facets[machineName].filter) + const values = searchParams.getAll(facets[machineName as keyof typeof facets].filter) if (values.length > 0) { return { ...acc, - [facets[machineName].filter]: [...values], + [facets[machineName as keyof typeof facets].filter]: [...values], } } return acc @@ -52,7 +51,7 @@ export const getFacetsForSearchRequest = (searchParams: ReadonlyURLSearchParams) export const getNextPageParamsFunc = ( currentPage: number ): GetNextPageParamFunction => { - const limit = goConfig("search.item.limit") + const limit = goConfig("search.item.limit") return ({ search: { hitcount } }) => { const totalPages = Math.ceil(hitcount / limit) diff --git a/components/shared/searchFilters/helper.ts b/components/shared/searchFilters/helper.ts index d84b8a9a..4e7d1743 100644 --- a/components/shared/searchFilters/helper.ts +++ b/components/shared/searchFilters/helper.ts @@ -2,8 +2,7 @@ import { flatten } from "lodash" import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime" import { ReadonlyURLSearchParams } from "next/navigation" -import goConfig from "@/lib/config/config" -import { TConfigSearchFacets } from "@/lib/config/resolvers/search" +import goConfig from "@/lib/config/goConfig" import { FacetFieldEnum, SearchFacetFragment } from "@/lib/graphql/generated/fbi/graphql" import { TContext, TFilters } from "@/lib/machines/search/types" @@ -44,14 +43,14 @@ export const sortByActiveFacets = (facet: SearchFacetFragment, selectedFilters: } export const getFacetMachineNames = () => { - const facets = goConfig>("search.facets") + const facets = goConfig("search.facets") return Object.keys(facets) as FacetFieldEnum[] } export const getFacetTranslation = (facetFilter: keyof TFilters) => { - const facets = goConfig("search.facets") + const facets = goConfig("search.facets") - return facets[facetFilter.toUpperCase() as keyof TConfigSearchFacets].translation || "" + return facets[facetFilter.toUpperCase() as keyof typeof facets].translation || "" } export const getActiveFilters = ( diff --git a/lib/config/config.ts b/lib/config/config.ts deleted file mode 100644 index 2cb3f241..00000000 --- a/lib/config/config.ts +++ /dev/null @@ -1,39 +0,0 @@ -import MissingConfigurationError from "./errors/MissingConfigurationError" -import app from "./resolvers/app" -import search from "./resolvers/search" -import serviceDplCms from "./resolvers/service.dpl-cms" -import serviceFbi from "./resolvers/service.fbi" -import serviceUnilogin from "./resolvers/service.unilogin" -import token from "./resolvers/token" - -const resolvers = { - ...app, - ...serviceDplCms, - ...serviceFbi, - ...serviceUnilogin, - ...search, - ...token, -} - -const retrieveValue = (key: keyof typeof resolvers) => { - if (key in resolvers) { - if (typeof resolvers[key] !== "function") { - return resolvers[key] - } - return resolvers[key]() - } - - return null -} - -const goConfig = (key: keyof typeof resolvers) => { - const value = retrieveValue(key) as TValue - - if (!value && value !== 0) { - throw new MissingConfigurationError(`Missing configuration for ${key}`) - } - - return value -} - -export default goConfig diff --git a/lib/config/dpl-cms/dplCmsConfig.ts b/lib/config/dpl-cms/dplCmsConfig.ts new file mode 100644 index 00000000..dba2df56 --- /dev/null +++ b/lib/config/dpl-cms/dplCmsConfig.ts @@ -0,0 +1,32 @@ +import { QueryClient } from "@tanstack/react-query" + +import { + GetDplCmsConfigurationQuery, + useGetDplCmsConfigurationQuery, +} from "@/lib/graphql/generated/dpl-cms/graphql" + +const queryDplCmsConfig = async (queryClient: QueryClient) => { + const { dplConfiguration } = await queryClient.fetchQuery({ + queryKey: useGetDplCmsConfigurationQuery.getKey(), + queryFn: useGetDplCmsConfigurationQuery.fetcher(), + // Cache 5 minutes unless invalidated + staleTime: 60 * 5, + }) + + return dplConfiguration ?? null +} + +// eslint-disable-next-line prefer-const +let dplCmsConfigClient = new QueryClient({}) + +const getDplCmsConfig = async () => { + const result = await queryDplCmsConfig(dplCmsConfigClient) + + return result +} + +export const getDplCmsUniloginConfig = async () => { + const config = await getDplCmsConfig() + + return config?.unilogin ?? null +} diff --git a/lib/config/goConfig.ts b/lib/config/goConfig.ts new file mode 100644 index 00000000..ab13d946 --- /dev/null +++ b/lib/config/goConfig.ts @@ -0,0 +1,34 @@ +import MissingConfigurationError from "./errors/MissingConfigurationError" +import { TConfigKey, TResolvers, resolvers } from "./resolvers" + +export const retrieveValue = < + TConfigResolver extends TResolvers, + TConfigResolverKey extends keyof TConfigResolver, +>( + key: TConfigResolverKey, + resolvers: TConfigResolver +) => { + if (key in resolvers) { + if (typeof resolvers[key as TConfigResolverKey] !== "function") { + return resolvers[key as TConfigResolverKey] + } + const resolver = resolvers[key as TConfigResolverKey] + if (typeof resolver === "function") { + return resolver() + } + } + + return null +} + +const goConfig = (key: K) => { + const value = retrieveValue(key, resolvers) + + if (!value && value !== 0) { + throw new MissingConfigurationError(`Missing configuration for ${key}`) + } + + return value as TResolvers[K] extends () => infer R ? R : TResolvers[K] +} + +export default goConfig diff --git a/lib/config/resolvers/index.ts b/lib/config/resolvers/index.ts new file mode 100644 index 00000000..3c498f96 --- /dev/null +++ b/lib/config/resolvers/index.ts @@ -0,0 +1,16 @@ +import app from "./app" +import search from "./search" +import serviceFbi from "./service.fbi" +import serviceUnilogin from "./service.unilogin" +import token from "./token" + +export const resolvers = { + ...app, + ...serviceFbi, + ...serviceUnilogin, + ...search, + ...token, +} + +export type TResolvers = typeof resolvers +export type TConfigKey = keyof TResolvers diff --git a/lib/config/resolvers/service.dpl-cms.ts b/lib/config/resolvers/service.dpl-cms.ts deleted file mode 100644 index 89f321a6..00000000 --- a/lib/config/resolvers/service.dpl-cms.ts +++ /dev/null @@ -1,9 +0,0 @@ -const serviceDplCms = { - "service.dpl-cms.graphql.endpoint": () => { - if (process.env.NEXT_PUBLIC_GRAPHQL_SCHEMA_ENDPOINT_DPL_CMS) { - return process.env.NEXT_PUBLIC_GRAPHQL_SCHEMA_ENDPOINT_DPL_CMS - } - }, -} - -export default serviceDplCms diff --git a/lib/config/resolvers/service.unilogin.ts b/lib/config/resolvers/service.unilogin.ts index 26f31019..71584929 100644 --- a/lib/config/resolvers/service.unilogin.ts +++ b/lib/config/resolvers/service.unilogin.ts @@ -1,28 +1,45 @@ +import { getDplCmsUniloginConfig } from "../dpl-cms/dplCmsConfig" + const serviceUnilogin = { - "service.unilogin.api.url": () => { + "service.unilogin.api.url": async () => { if (process.env.UNILOGIN_API_URL) { return process.env.UNILOGIN_API_URL } + + const config = await getDplCmsUniloginConfig() + if (config?.unilogin_api_url) { + return config?.unilogin_api_url + } }, - "service.unilogin.wellknown.url": () => { + "service.unilogin.wellknown.url": async () => { if (process.env.UNILOGIN_WELLKNOWN_URL) { return process.env.UNILOGIN_WELLKNOWN_URL } - }, - "service.unilogin.refresh-token.url": () => { - if (process.env.UNILOGIN_REFRESH_TOKEN_URL) { - return process.env.UNILOGIN_REFRESH_TOKEN_URL + + const config = await getDplCmsUniloginConfig() + if (config?.unilogin_api_wellknown_url) { + return config?.unilogin_api_wellknown_url } }, - "service.unilogin.client-id": () => { + "service.unilogin.client-id": async () => { if (process.env.UNILOGIN_CLIENT_ID) { return process.env.UNILOGIN_CLIENT_ID } + + const config = await getDplCmsUniloginConfig() + if (config?.unilogin_api_client_id) { + return config?.unilogin_api_client_id + } }, - "service.unilogin.client-secret": () => { + "service.unilogin.client-secret": async () => { if (process.env.UNILOGIN_CLIENT_SECRET) { return process.env.UNILOGIN_CLIENT_SECRET } + + const config = await getDplCmsUniloginConfig() + if (config?.unilogin_api_client_secret) { + return config?.unilogin_api_client_secret + } }, "service.unilogin.session.secret": () => { if (process.env.UNILOGIN_SESSION_SECRET) { diff --git a/lib/graphql/fetchers/dpl-cms.fetcher.ts b/lib/graphql/fetchers/dpl-cms.fetcher.ts new file mode 100644 index 00000000..cfc4df4f --- /dev/null +++ b/lib/graphql/fetchers/dpl-cms.fetcher.ts @@ -0,0 +1,36 @@ +export function fetcher( + query: string, + variables?: TVariables, + options?: RequestInit["headers"] +) { + const dplCmsGraphqlEndpoint = process.env.NEXT_PUBLIC_GRAPHQL_SCHEMA_ENDPOINT_DPL_CMS + const dplCmsGraphqlBasicToken = process.env.NEXT_PUBLIC_GRAPHQL_BASIC_TOKEN_DPL_CMS + + if (!dplCmsGraphqlEndpoint || !dplCmsGraphqlBasicToken) { + throw new Error("Missing DPL CMS GraphQL endpoint or basic token") + } + + return async (): Promise => { + const res = await fetch(dplCmsGraphqlEndpoint, { + method: "POST", + ...{ + headers: { + "Content-Type": "application/json", + Authorization: `Basic ${dplCmsGraphqlBasicToken}`, + ...options, + }, + }, + body: JSON.stringify({ query, variables }), + }) + + const json = await res.json() + + if (json.errors) { + const { message } = json.errors[0] + + throw new Error(message) + } + + return json.data + } +} diff --git a/lib/graphql/fetchers/fbi.fetcher.ts b/lib/graphql/fetchers/fbi.fetcher.ts index 68b7672b..5fed3532 100644 --- a/lib/graphql/fetchers/fbi.fetcher.ts +++ b/lib/graphql/fetchers/fbi.fetcher.ts @@ -1,4 +1,4 @@ -import goConfig from "@/lib/config/config" +import goConfig from "@/lib/config/goConfig" export const fetchData = ( query: string, diff --git a/lib/graphql/generated/dpl-cms/graphql.tsx b/lib/graphql/generated/dpl-cms/graphql.tsx index c5229fce..55fdbd3b 100644 --- a/lib/graphql/generated/dpl-cms/graphql.tsx +++ b/lib/graphql/generated/dpl-cms/graphql.tsx @@ -1,4 +1,5 @@ -import { useQuery, UseQueryOptions, useSuspenseQuery, UseSuspenseQueryOptions } from '@tanstack/react-query'; +import { useQuery, useSuspenseQuery, UseQueryOptions, UseSuspenseQueryOptions } from '@tanstack/react-query'; +import { fetcher } from '@/lib/graphql/fetchers/dpl-cms.fetcher'; export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; @@ -6,26 +7,6 @@ export type MakeOptional = Omit & { [SubKey in K]?: export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; export type MakeEmpty = { [_ in K]?: never }; export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; - -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"}}), - body: JSON.stringify({ query, variables }), - }); - - const json = await res.json(); - - if (json.errors) { - const { message } = json.errors[0]; - - throw new Error(message); - } - - return json.data; - } -} /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { ID: { input: string; output: string; } @@ -92,6 +73,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'; @@ -556,6 +543,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 */ @@ -783,6 +772,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 +798,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 GetDplCmsConfigurationQueryVariables = Exact<{ [key: string]: never; }>; + + +export type GetDplCmsConfigurationQuery = { __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 = ` @@ -856,4 +859,56 @@ export const useSuspenseGetArticleQuery = < useSuspenseGetArticleQuery.getKey = (variables: GetArticleQueryVariables) => ['getArticleSuspense', variables]; -useGetArticleQuery.fetcher = (variables: GetArticleQueryVariables) => fetcher(GetArticleDocument, variables); +useGetArticleQuery.fetcher = (variables: GetArticleQueryVariables, options?: RequestInit['headers']) => fetcher(GetArticleDocument, variables, options); + +export const GetDplCmsConfigurationDocument = ` + query getDplCmsConfiguration { + dplConfiguration { + unilogin { + unilogin_api_url + unilogin_api_wellknown_url + unilogin_api_client_id + unilogin_api_client_secret + } + } +} + `; + +export const useGetDplCmsConfigurationQuery = < + TData = GetDplCmsConfigurationQuery, + TError = unknown + >( + variables?: GetDplCmsConfigurationQueryVariables, + options?: Omit, 'queryKey'> & { queryKey?: UseQueryOptions['queryKey'] } + ) => { + + return useQuery( + { + queryKey: variables === undefined ? ['getDplCmsConfiguration'] : ['getDplCmsConfiguration', variables], + queryFn: fetcher(GetDplCmsConfigurationDocument, variables), + ...options + } + )}; + +useGetDplCmsConfigurationQuery.getKey = (variables?: GetDplCmsConfigurationQueryVariables) => variables === undefined ? ['getDplCmsConfiguration'] : ['getDplCmsConfiguration', variables]; + +export const useSuspenseGetDplCmsConfigurationQuery = < + TData = GetDplCmsConfigurationQuery, + TError = unknown + >( + variables?: GetDplCmsConfigurationQueryVariables, + options?: Omit, 'queryKey'> & { queryKey?: UseSuspenseQueryOptions['queryKey'] } + ) => { + + return useSuspenseQuery( + { + queryKey: variables === undefined ? ['getDplCmsConfigurationSuspense'] : ['getDplCmsConfigurationSuspense', variables], + queryFn: fetcher(GetDplCmsConfigurationDocument, variables), + ...options + } + )}; + +useSuspenseGetDplCmsConfigurationQuery.getKey = (variables?: GetDplCmsConfigurationQueryVariables) => variables === undefined ? ['getDplCmsConfigurationSuspense'] : ['getDplCmsConfigurationSuspense', variables]; + + +useGetDplCmsConfigurationQuery.fetcher = (variables?: GetDplCmsConfigurationQueryVariables, options?: RequestInit['headers']) => fetcher(GetDplCmsConfigurationDocument, variables, options); diff --git a/lib/graphql/generated/fbi/graphql.tsx b/lib/graphql/generated/fbi/graphql.tsx index 1f07793c..5647b53a 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 */ @@ -199,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>; }; @@ -282,7 +282,7 @@ export type CopyRequestInput = { export type CopyRequestResponse = { __typename?: 'CopyRequestResponse'; - status: CopyRequestStatusEnum; + status: CopyRequestStatusEnum | '%future added value'; }; export enum CopyRequestStatusEnum { @@ -321,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; }; @@ -456,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; }; @@ -481,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']; }; @@ -495,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']; }; @@ -556,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']; }; @@ -607,7 +607,7 @@ export type InfomediaResponse = { __typename?: 'InfomediaResponse'; article?: Maybe; /** Infomedia error */ - error?: Maybe; + error?: Maybe; }; export type InfomediaService = { @@ -707,7 +707,7 @@ export type LinkCheckResponse = { __typename?: 'LinkCheckResponse'; brokenSince?: Maybe; lastCheckedAt?: Maybe; - status: LinkCheckStatusEnum; + status: LinkCheckStatusEnum | '%future added value'; url: Scalars['String']['output']; }; @@ -821,7 +821,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; }; @@ -858,7 +858,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 = { @@ -923,12 +923,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>; @@ -1058,7 +1058,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; }; @@ -1087,7 +1087,7 @@ export type NarrativeTechnique = SubjectInterface & { display: Scalars['String']['output']; language?: Maybe; local?: Maybe; - type: SubjectTypeEnum; + type: SubjectTypeEnum | '%future added value'; }; export type Note = { @@ -1097,7 +1097,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 { @@ -1159,7 +1159,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 = { @@ -1439,7 +1439,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 { @@ -1462,7 +1462,7 @@ export type Role = { export type SchoolUse = { __typename?: 'SchoolUse'; - code: SchoolUseCodeEnum; + code: SchoolUseCodeEnum | '%future added value'; display: Scalars['String']['output']; }; @@ -1493,7 +1493,7 @@ export type SearchFiltersInput = { mainLanguages?: InputMaybe>; materialTypesGeneral?: InputMaybe>; materialTypesSpecific?: InputMaybe>; - status?: InputMaybe>; + status?: InputMaybe>; subjects?: InputMaybe>; sublocation?: InputMaybe>; workTypes?: InputMaybe>; @@ -1611,7 +1611,7 @@ export type Setting = SubjectInterface & { display: Scalars['String']['output']; language?: Maybe; local?: Maybe; - type: SubjectTypeEnum; + type: SubjectTypeEnum | '%future added value'; }; export type Shelfmark = { @@ -1624,7 +1624,7 @@ export type Shelfmark = { export type SortInput = { index: Scalars['String']['input']; - order: SortOrderEnum; + order: SortOrderEnum | '%future added value'; }; export enum SortOrderEnum { @@ -1654,7 +1654,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 & { @@ -1662,7 +1662,7 @@ export type SubjectText = SubjectInterface & { display: Scalars['String']['output']; language?: Maybe; local?: Maybe; - type: SubjectTypeEnum; + type: SubjectTypeEnum | '%future added value'; }; export enum SubjectTypeEnum { @@ -1701,7 +1701,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 = { @@ -1714,7 +1714,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; }; @@ -1739,7 +1739,7 @@ export type TimePeriod = SubjectInterface & { language?: Maybe; local?: Maybe; period: Range; - type: SubjectTypeEnum; + type: SubjectTypeEnum | '%future added value'; }; export type Translation = { @@ -1813,7 +1813,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; }; @@ -1879,7 +1879,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/configuration.dpl-cms.graphql b/lib/graphql/queries/configuration.dpl-cms.graphql new file mode 100644 index 00000000..a3ae9a36 --- /dev/null +++ b/lib/graphql/queries/configuration.dpl-cms.graphql @@ -0,0 +1,10 @@ +query getDplCmsConfiguration { + dplConfiguration { + unilogin { + unilogin_api_url + unilogin_api_wellknown_url + unilogin_api_client_id + unilogin_api_client_secret + } + } +} diff --git a/lib/machines/search/useSearchMachineActor.tsx b/lib/machines/search/useSearchMachineActor.tsx index 360d5dfc..d4c3ebff 100644 --- a/lib/machines/search/useSearchMachineActor.tsx +++ b/lib/machines/search/useSearchMachineActor.tsx @@ -8,15 +8,15 @@ import { useEffect, useRef, useState } from "react" import { AnyEventObject, createActor } from "xstate" import { getFacetsForSearchRequest } from "@/components/pages/searchPageLayout/helper" -import goConfig from "@/lib/config/config" +import goConfig from "@/lib/config/goConfig" import searchMachine from "./search.machine" const searchActor = createActor(searchMachine, { input: { - initialOffset: goConfig("search.offset.initial"), - searchPageSize: goConfig("search.item.limit"), - facetLimit: goConfig("search.facet.limit"), + initialOffset: goConfig("search.offset.initial"), + searchPageSize: goConfig("search.item.limit"), + facetLimit: goConfig("search.facet.limit"), }, }).start() diff --git a/lib/rest/cover-service-api/mutator/fetcher.ts b/lib/rest/cover-service-api/mutator/fetcher.ts index a2975227..7d429607 100644 --- a/lib/rest/cover-service-api/mutator/fetcher.ts +++ b/lib/rest/cover-service-api/mutator/fetcher.ts @@ -1,4 +1,4 @@ -import goConfig from "@/lib/config/config" +import goConfig from "@/lib/config/goConfig" import { getRestServiceUrlWithParams } from "../../../fetchers/helper" diff --git a/lib/rest/publizon-api/mutator/fetcher.ts b/lib/rest/publizon-api/mutator/fetcher.ts index 438c048c..73184931 100644 --- a/lib/rest/publizon-api/mutator/fetcher.ts +++ b/lib/rest/publizon-api/mutator/fetcher.ts @@ -1,4 +1,4 @@ -import goConfig from "@/lib/config/config" +import goConfig from "@/lib/config/goConfig" import { getRestServiceUrlWithParams } from "@/lib/fetchers/helper" export const fetcher = async ({ diff --git a/lib/session/oauth/uniloginClient.ts b/lib/session/oauth/uniloginClient.ts index 8f57cb0b..e4b05cec 100644 --- a/lib/session/oauth/uniloginClient.ts +++ b/lib/session/oauth/uniloginClient.ts @@ -1,16 +1,16 @@ import * as client from "openid-client" -import goConfig from "@/lib/config/config" - -const appUrl = goConfig("app.url") +import goConfig from "@/lib/config/goConfig" export const uniloginClientSettings = { - post_login_route: `${appUrl}/user/profile`, + post_login_route: `${goConfig("app.url")}/user/profile`, } export async function getUniloginClientConfig() { - const server: URL = new URL(goConfig("service.unilogin.wellknown.url")) - const clientId: string = goConfig("service.unilogin.client-id") - const clientSecret: string = goConfig("service.unilogin.client-secret") + const wellKnownUrl = await goConfig("service.unilogin.wellknown.url") + + const server: URL = new URL(String(wellKnownUrl)) + const clientId = (await goConfig("service.unilogin.client-id")) ?? "" + const clientSecret = (await goConfig("service.unilogin.client-secret")) ?? "" return await client.discovery(server, clientId, clientSecret) } diff --git a/lib/session/session.ts b/lib/session/session.ts index 119fac55..79e05565 100644 --- a/lib/session/session.ts +++ b/lib/session/session.ts @@ -3,12 +3,12 @@ import { IronSession, SessionOptions, getIronSession } from "iron-session" import { cookies } from "next/headers" import { NextRequest, NextResponse } from "next/server" -import goConfig from "../config/config" +import goConfig from "../config/goConfig" import { TSessionType, TTokenSet } from "../types/session" export const sessionOptions: SessionOptions = { // TODO: generate a random password and store it in a secure place - password: goConfig("service.unilogin.session.secret"), + password: String(goConfig("service.unilogin.session.secret")), cookieName: "go-session", cookieOptions: { // secure only works in `https` environments diff --git a/tsconfig.json b/tsconfig.json index 15417281..115c1a8b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -33,6 +33,6 @@ "node_modules", "lib/rest/cover-service-api/model", "lib/rest/cover-service-api/cover-service.ts", - "lib/graphql/generated/fbi/*.tsx" + "lib/graphql/generated/**/*.tsx" ] }