Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add UniLogin configuration query and update related services #48

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
bdd8bf7
feat: add UniLogin configuration query and update related services
ThomasGross Nov 15, 2024
b6dbb97
feat: add type checking and CI check scripts to package.json
ThomasGross Nov 15, 2024
c52b5c6
fix: correct ci-check script to include yarn format:check
ThomasGross Nov 15, 2024
10e51fa
fix: add TypeScript ignore comments for mocked configurations in sear…
ThomasGross Nov 15, 2024
006f1e5
feat: implement ConfigProvider and integrate UniLogin configuration h…
ThomasGross Nov 18, 2024
2d8e1e2
feat: add route handler that fetches configs
ThomasGross Nov 18, 2024
2df9071
feat: refactor Unilogin client integration to fetch configuration dyn…
ThomasGross Nov 19, 2024
c478d95
refactor: remove commented-out code for getUniloginClient function
ThomasGross Nov 19, 2024
48f5946
Merge branch 'main' into DDFBRA-97-frontend-hent-konfig-objekt-fra-ba…
ThomasGross Nov 19, 2024
fe2d943
Cleaning up .env files
spaceo Nov 20, 2024
8b2d37e
Skip refresh token tests and middleware
spaceo Nov 20, 2024
8d07a26
Moving configuration fetching inside getUniloginClient
spaceo Nov 20, 2024
439ca50
Add missing unilogin conf files
spaceo Nov 20, 2024
f8e5591
Merge pull request #52 from danskernesdigitalebibliotek/DDFBRA-97-fro…
spaceo Nov 21, 2024
2f75108
Enable goConfig to infer its return type
spaceo Nov 21, 2024
040681e
Introducing server-only config and useGoConfig hook
spaceo Nov 22, 2024
362d10c
Merge branch 'main' into DDFBRA-97-frontend-hent-konfig-objekt-fra-ba…
spaceo Nov 23, 2024
1855a4a
Simplify usGoConfig hook.
spaceo Nov 23, 2024
bb60eea
Remove remains of config store context system
spaceo Nov 23, 2024
523a74d
Bring .env files up to date
spaceo Nov 23, 2024
594a776
Remove unused import
spaceo Nov 23, 2024
1207e38
Add all graphql codegen generated files to ts excluded
spaceo Nov 23, 2024
60b41b9
Put config tests into describe suite
spaceo Nov 24, 2024
37ca25a
Only fectch unilogin config when resolver functions are invoked
spaceo Nov 24, 2024
1fd4647
Adding missing fetcher options
spaceo Nov 24, 2024
17b1903
Getting rid of hook and server-only concept
spaceo Nov 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
@@ -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
12 changes: 6 additions & 6 deletions .env.test
Original file line number Diff line number Diff line change
@@ -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
19 changes: 11 additions & 8 deletions __tests__/config.test.ts
Original file line number Diff line number Diff line change
@@ -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")
})
})
4 changes: 2 additions & 2 deletions __tests__/search.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion app/auth/callback/unilogin/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion app/auth/login/unilogin/route.ts
Original file line number Diff line number Diff line change
@@ -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"

Expand Down
8 changes: 4 additions & 4 deletions app/auth/logout/route.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
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()

// TODO: Distinguish between session types here.
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

Expand All @@ -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`)
}
4 changes: 2 additions & 2 deletions app/auth/token/refresh/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -14,7 +14,7 @@ const sessionTokenSchema = z.object({
})

export async function GET(request: NextRequest, response: NextResponse) {
const appUrl = goConfig<string>("app.url")
const appUrl = String(goConfig("app.url"))
const config = await getUniloginClientConfig()
// TODO: Fix refresh token flow with new openid-client.

Expand Down
4 changes: 4 additions & 0 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div
className="grid min-h-screen grid-rows-[20px_1fr_20px] items-center justify-items-center gap-16 p-8 pb-20
font-[family-name:var(--font-geist-sans)] sm:p-20">
<main className="row-start-2 flex flex-col items-center gap-8 sm:items-start">
<pre>Wellknown url: {wellknownUrl}</pre>
<Image
className="dark:invert"
src="https://nextjs.org/icons/next.svg"
Expand Down
2 changes: 1 addition & 1 deletion app/search/fetchSearchResult.server.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { QueryClient } from "@tanstack/react-query"

import { getFacetMachineNames } from "@/components/shared/searchFilters/helper"
import goConfig from "@/lib/config/config"
import goConfig from "@/lib/config/goConfig"
import {
useSearchFacetsQuery,
useSearchWithPaginationQuery,
Expand Down
42 changes: 38 additions & 4 deletions codegen.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { CodegenConfig } from "@graphql-codegen/cli"

import goConfig from "./lib/config/config"
import goConfig from "./lib/config/goConfig"

const { loadEnvConfig } = require("@next/env")

Expand All @@ -9,11 +9,45 @@ 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: {
[`${process.env.NEXT_PUBLIC_GRAPHQL_SCHEMA_ENDPOINT_DPL_CMS}`]: {
headers: {
Authorization: `Basic ${process.env.GRAPHQL_SCHEMA_ENDPOINT_BASIC_TOKEN_DPL_CMS}`,
},
},
},
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: "@/lib/graphql/fetchers/dpl-cms.fetcher#fetcher",
},
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: [
{
[goConfig<string>("service.fbi.graphql.endpoint")]: {
[String(goConfig("service.fbi.graphql.endpoint"))]: {
headers: {
Authorization: `Bearer ${goConfig("token.adgangsplatformen.library")}`,
},
Expand All @@ -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,
Expand Down
15 changes: 7 additions & 8 deletions components/pages/searchPageLayout/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -18,29 +17,29 @@ export const getSearchQueryArguments = ({
currentPage: number
facetFilters: SearchFiltersInput
}) => {
const limit = goConfig<number>("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<TConfigSearchFacets>("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
Expand All @@ -52,7 +51,7 @@ export const getFacetsForSearchRequest = (searchParams: ReadonlyURLSearchParams)
export const getNextPageParamsFunc = (
currentPage: number
): GetNextPageParamFunction<number, SearchWithPaginationQuery> => {
const limit = goConfig<number>("search.item.limit")
const limit = goConfig("search.item.limit")

return ({ search: { hitcount } }) => {
const totalPages = Math.ceil(hitcount / limit)
Expand Down
9 changes: 4 additions & 5 deletions components/shared/searchFilters/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -44,14 +43,14 @@ export const sortByActiveFacets = (facet: SearchFacetFragment, selectedFilters:
}

export const getFacetMachineNames = () => {
const facets = goConfig<Record<string, unknown>>("search.facets")
const facets = goConfig("search.facets")
return Object.keys(facets) as FacetFieldEnum[]
}

export const getFacetTranslation = (facetFilter: keyof TFilters) => {
const facets = goConfig<TConfigSearchFacets>("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 = (
Expand Down
39 changes: 0 additions & 39 deletions lib/config/config.ts

This file was deleted.

32 changes: 32 additions & 0 deletions lib/config/dpl-cms/dplCmsConfig.ts
Original file line number Diff line number Diff line change
@@ -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<GetDplCmsConfigurationQuery>({
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
}
Loading