@biblioteksentralen/bmdb-search 0.0.0-beta.4 → 0.0.0-beta.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
1
  # @biblioteksentralen/bmdb-search
2
2
 
3
- TODO
3
+ ## 0.0.0-beta.16
4
+
5
+ - [BREAKING] `/{profile}` prefix for all routes
6
+ - Removed `clientIdentifier`
7
+
8
+ ## 0.0.0-beta.14
9
+
10
+ - Refactored `Work.subjects` model
package/README.md CHANGED
@@ -2,75 +2,147 @@
2
2
 
3
3
  TypeScript client for searching Bibliotekenes metadatabrønn (BMDB) using [openapi-fetch](https://openapi-ts.dev/openapi-fetch/).
4
4
 
5
- ## Options
5
+ ## Setup
6
6
 
7
- ### Client identification policy
7
+ ### Setup for server-side usage
8
8
 
9
- The API does not require authentication, but clients should identify themselves using a descriptive
10
- name and a contact address in the `clientIdentifier` string. We will only contact you about usage of the API.
9
+ If the client is running in an environment that can keep secrets, i.e. server-side, the bundled
10
+ `clientCredentialsAccessTokenGetter` helper can be used to exchange your long-lived OAuth 2.0
11
+ client credentials for short-lived access tokens:
11
12
 
12
- ## Usage examples
13
+ ```typescript
14
+ import { createBmdbFetchClient } from "@biblioteksentralen/bmdb-search/v1";
15
+ import { clientCredentialsAccessTokenGetter } from "@biblioteksentralen/bmdb-search/server";
16
+
17
+ const bmdbSearchClient = createBmdbFetchClient({
18
+ host: "https://search.bmdb.no",
19
+ auth: clientCredentialsAccessTokenGetter({
20
+ clientId: process.env.BMDB_CORDATA_API_CLIENT_ID,
21
+ clientSecret: process.env.BMDB_CORDATA_API_CLIENT_SECRET,
22
+ }),
23
+ });
24
+ ```
13
25
 
14
- ### Search works
26
+ Note: If you are planning to access pre-production environments, see notes below on how to
27
+ connect to those below.
28
+
29
+ ### Setup for client-side (frontend) usage
30
+
31
+ To use the BMDB search client in a client-side environment, i.e. one that cannot keep secrets,
32
+ the client needs to fetch access tokens from a server-side route that you manage.
33
+
34
+ Example:
15
35
 
16
36
  ```typescript
17
- import { createBmdbFetchClient } from "@biblioteksentralen/bmdb-search";
37
+ // clients/bmdbClient.ts
38
+ import { createBmdbFetchClient } from "@biblioteksentralen/bmdb-search/v1";
39
+
40
+ export const bmdbBrowserClient = createBmdbFetchClient({
41
+ host: "https://search.bmdb.no",
42
+ auth: async () => {
43
+ // Fetches access token from an endpoint you manage
44
+ const response = await fetch("/api/bmdb/access-token");
18
45
 
19
- const bmdbSearchClient = createBmdbFetchClient({ clientIdentifier: "client-unique-description" });
20
- const { data, error } = await bmdbSearchClient.GET("/works/search", { params: { query: { query: "test" } } });
46
+ if (!response.ok) {
47
+ throw new Error("Failed to fetch BMDB access token");
48
+ }
49
+
50
+ const { accessToken } = await response.json();
51
+ return accessToken;
52
+ },
53
+ });
21
54
  ```
22
55
 
23
- The arguments and response are fully typed.
56
+ Note that the client's auth middleware caches tokens in memory, only calling the endpoint when a new
57
+ token is needed. For this reason, it's recommended to not create a new client for each request, but
58
+ reuse it.
24
59
 
25
- ### Usage with tanstack-query, openapi-react-query and next.js
60
+ The implementation of the server-side API route depends on the programming language and framework
61
+ you use. Below is a simple example for Next.js with App Router:
26
62
 
27
- Using [tanstack](https://tanstack.com/query/latest) for fetching with state management and [openapi-react-query](https://openapi-ts.dev/openapi-react-query/) for typing.
63
+ ```typescript
64
+ // app/api/bmdb/access-token/route.ts
65
+ import { clientCredentialsAccessTokenGetter } from "@biblioteksentralen/bmdb-search/server";
66
+
67
+ const getAccessToken = clientCredentialsAccessTokenGetter({
68
+ clientId: process.env.BMDB_CORDATA_API_CLIENT_ID,
69
+ clientSecret: process.env.BMDB_CORDATA_API_CLIENT_SECRET,
70
+ });
71
+
72
+ export async function GET() {
73
+ try {
74
+ // Ask yourself if you want to restrict clients before returning a token
75
+ const accessToken = await getAccessToken();
76
+ return Response.json({ accessToken });
77
+ } catch (err) {
78
+ console.error(`Failed to fetch access token`, err);
79
+ return Response.json({ error: "Failed to fetch access token" }, { status: 500 });
80
+ }
81
+ }
82
+ ```
83
+
84
+ ### Connecting to pre-production environments (dev/test/staging)
28
85
 
29
- Add a rewrite in the next.js config file to be able to use the frontend host without violating the content service policy:
86
+ When connecting to pre-production environments, the OAuth 2.0 Authorization Server Token URL must be
87
+ be specified manually through the `tokenUrl` parameter, and the audience must be specified through the
88
+ `audience` parameter. Example for connecting to the test/staging environment:
30
89
 
31
90
  ```typescript
32
- // next.config.js or similar
33
- const searchApiHost = "https://search.data.bs.no";
34
-
35
- const config = {
36
- // ... other config ...
37
- rewrites() {
38
- return [
39
- {
40
- source: "/bmdb/api/:path*",
41
- destination: `${searchApiHost}/bmdb/api/:path*`,
42
- },
43
- ];
44
- },
45
- };
91
+ const bmdbSearchClient = createBmdbFetchClient({
92
+ host: "https://search-staging.bmdb.no",
93
+ auth: clientCredentialsAccessTokenGetter({
94
+ clientId: process.env.BMDB_CORDATA_API_CLIENT_ID,
95
+ clientSecret: process.env.BMDB_CORDATA_API_CLIENT_SECRET,
96
+ tokenUrl: "https://biblioteksentralen-uat.eu.kinde.com/oauth2/token",
97
+ audience: " https://search-staging.bmdb.no/cordata/",
98
+ }),
99
+ });
46
100
  ```
47
101
 
48
- If necessary, skip in middleware to exempt the API paths from internationalization etc:
102
+ ## Usage
103
+
104
+ ### Search works
49
105
 
50
106
  ```typescript
51
- // middleware.ts
52
- export const config = {
53
- matcher: [
54
- // ...other
55
- "/((?!|bmdb/api|_next/static).*)",
56
- ],
57
- };
107
+ const { data, error } = await bmdbSearchClient.GET("/{searchProfile}/works/search", {
108
+ params: {
109
+ path: { searchProfile: "global" },
110
+ query: { query: "test" },
111
+ },
112
+ });
58
113
  ```
59
114
 
60
- Create hooks and fetch data:
115
+ ### Get works
61
116
 
62
117
  ```typescript
63
- import { createBmdbFetchClient } from "@biblioteksentralen/bmdb-search";
64
- import createReactQueryClient from "openapi-react-query";
118
+ const { data, error } = await bmdbSearchClient.GET("/{searchProfile}/works", {
119
+ params: {
120
+ path: { searchProfile: "deichman" },
121
+ query: { ids: "1237715,1237713" },
122
+ },
123
+ });
124
+ ```
65
125
 
66
- const bmdbSearchClient = createBmdbFetchClient({ clientIdentifier: "client-unique-description" });
67
- const { useQuery } = createReactQueryClient(bmdbSearchClient);
126
+ ### Search agents
68
127
 
69
- const Component = () => {
70
- const { data, error, isLoading } = useQuery("get", "/works/search", { params: { query: { query: "test" } } });
71
- if (isLoading) return <div>Loading</div>;
72
- if (error) return <div>Something went wrong</div>;
73
- return <div>Found {data?.total} works</div>;
74
- };
128
+ ```typescript
129
+ const { data, error } = await bmdbSearchClient.GET("/{searchProfile}/agents/search", {
130
+ params: {
131
+ path: { searchProfile: "tonsberg" },
132
+ query: { query: "test" },
133
+ },
134
+ });
135
+ ```
136
+
137
+ ### Find all contributor roles for a given agent
75
138
 
139
+ ```typescript
140
+ const { data, error } = await bmdbSearchClient.GET("/{searchProfile}/agents/search", {
141
+ params: {
142
+ path: { searchProfile: "global" },
143
+ query: { facet: ["role"], size: 0, facet_terms_size: 20 },
144
+ },
145
+ });
76
146
  ```
147
+
148
+ The arguments and response are fully typed.
@@ -0,0 +1,7 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ export { __export };