Skip to content

Commit

Permalink
Merge pull request #257 from nulib/3508-reading-room-bug
Browse files Browse the repository at this point in the history
Only include Reading Room message on restricted Works. Update README
  • Loading branch information
adamjarling authored Mar 17, 2023
2 parents ba1335a + de5ef55 commit a95a37c
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 89 deletions.
128 changes: 67 additions & 61 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,89 @@
# Digital Collections v2 NextJS App
# Digital Collections v2

This is a work in progress, for experimenting with NextJS, AWS Amplify and various ways of building / data fetching / hosting.
Digital Collections v2 (DCv2) is a UI application for discovering and interacting with Collections and Works in NUL's repository.

## Getting Started
## Tech Stack
- [NextJS](https://nextjs.org/) React JS fullstack framework
- [TypeScript](https://www.typescriptlang.org/) for type safety
- [Radix UI](https://www.radix-ui.com/) A library of React primitives for accessibility and modular development
- [Stitches.dev](https://stitches.dev/) CSS in JS
- [IIIF](https://iiif.io/) Research APIs and Specs our data conforms to for open access.
- [AWS Amplify](https://aws.amazon.com/amplify/) Hosting environment
- [OpenSearch](https://opensearch.org/) Search index


## Development Environments
### Local
Install dependencies and run a NextJS development server:

```bash
npm install
npm run dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser.
Open [http://devbox.library.northwestern.edu:3000](http://devbox.library.northwestern.edu:3000) in your browser.

You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
### AWS Developer Environment (Northwestern dev team only)
Open a remote SSH dev environment connection in VSCode.

## Deploying
`cd` into the `dc-nextjs` repository

### NextJS Environment
1. Open a new terminal.

Commits to the `deploy/staging` branch will trigger a build in an AWS Amplify Hosting solution.
2. Make sure port 3000 is open by running `sg show`. If you don't see port 3000, run `sg open all 3000`. View more in [AWS convenience scripts](https://github.com/nulib/aws-developer-environment#convenience-scripts).

Commits prefaced with `preview/branch-name-here` will deploy to a preview branch
3. Temporarily change the following line in (`dc-nextjs/server.js`):
```js

### Data fetching
// Change
const hostname = "devbox.library.northwestern.edu";
// ...to
const hostname = "localhost";
```
Install dependencies

Currently in the Amplify AWS environment (Dec 2022), note that SSR (Server Side Rendering), will not pass authentication JWT tokens properly.
```bash
npm install
npm run dev
```

The primary dynamic route pages (`items/[id]` and `collections/[id]`), will support [Incremental Static Regeneration](https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration), but we'll keep build time routes to statically generate at 1 for both a Work and Collection.
And now open your AWS dev environment URL (Northwestern developers only).

````

## Deploy Environments
### Staging

Commits (via merges) into the `deploy/staging` branch will trigger a build in AWS Amplify to the **staging** environment.

https://dc.rdc-staging.library.northwestern.edu/

Commits prefaced with `preview/branch-name-here` will deploy to a preview branch. The URL will be available within AWS Amplify. This is useful for sharing the feature with staff/users as a preview before committing to staging.

### Production

Commits (via merges) into the `main` branch will trigger a build in AWS Amplify to the **production** environment.

https://dc.library.northwestern.edu/

## Data fetching / APIs

The application makes network requests against the [DC API v2](https://github.com/nulib/dc-api-v2) to access repository data. By default, all metadata is returned in the application. Authenticated content's media (image/audio/video) will be protected and obscured from public access.

Behind the scenes, DC API v2 is using OpenSearch `v 1.2` or Elasticsearch `v 7.17`. (For documentation references). Network request urls with `?as=iiif` will return data in the shape of a [IIIF](https://iiif.io/) manifest.

### Viewing the Index (OpenSearch) directly
OpenSearch's data can be accessed directly via [Kibana](https://www.elastic.co/kibana/) by executing the following commands:

```bash
export AWS_PROFILE=staging
aws-adfs login --profile $AWS_PROFILE
es-proxy
```

The API supports both POST for searching and GET for Work and Collection items.

### Environment variables
The API endpoint is an environment variable which is accessed in a local dev environment via the `miscellany` Git repo.

## Code Quality

Expand Down Expand Up @@ -80,54 +134,6 @@ To run [Jest](https://jestjs.io/) w/ [React Testing-Library](https://testing-lib
npm run test
```
## API

### Notes

Currently DC v2 hits a new DC API v2 for it's indexed data.

`https://dcapi.rdc.library.northwestern.edu/docs/v2`

Behind the scenes, DC API v2 is using OpenSearch `v 1.2` or Elasticsearch `v 7.17`. (For documentation references).

### Endpoints

The API endpoint is an environment variable which is accessed in a local dev environment via the `miscellany` Git repo. The dev environment runs against Staging.

### Viewing OpenSearch data locally

In a local dev environment, to view API data coming from the index (for now), run the following:

```
export AWS_PROFILE=staging
aws-adfs login --profile $AWS_PROFILE
es-proxy
```

The API supports both POST for searching and GET for Work and Collection items.

### Search examples

```
# Search Works (default)
curl -X POST '[URL]/search' --data-binary '{"query": {"match_all": {}}, "_source": "id", "size": 1000}' | jq
# Search Collections
curl -X POST '[URL]/search/collections' --data-binary '{"query": {"match_all": {}}, "_source": "id", "size": 1000}' | jq
```

### Direct GET request endpoint examples

```
[URL]/works/4359936f-9091-499b-893f-b8e900db49ec
[URL]/collections/18ec4c6b-192a-4ab8-9903-ea0f393c35f7
[URL]/file-sets/ce1f6d18-8563-4f70-aabc-d4ce1688d8dc
```

See documentation in above link for more info

## Optimizations
`npm run analyze` will run the [Next Bundle Analyzer](https://github.com/vercel/next.js/tree/canary/packages/next-bundle-analyzer) to show snapshots of the app's bundled JS.
Expand Down
4 changes: 2 additions & 2 deletions components/Facets/Filter/GroupList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,10 @@ describe("FacetsGroupList component", () => {
* Looks like Radix puts this active state data attribute
* on the element.... good for testing against:)
*/
const tabActive = await screen.findAllByRole("tab", { exact: true });
const tabActive = await screen.findAllByRole("tab");
expect(tabActive[0].dataset.state).toEqual("active");

const tabInactive = await screen.findAllByRole("tab", { exact: true });
const tabInactive = await screen.findAllByRole("tab");
expect(tabInactive[1].dataset.state).toEqual("inactive");
});

Expand Down
28 changes: 18 additions & 10 deletions components/Work/ViewerWrapper.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,36 @@ describe("WorkViewerWrapper", () => {
});
});

it("renders an announcement when in the Reading Room", async () => {
it("renders an announcement when in the Reading Room only when the Work is protected", async () => {
const readingUserContext = { ...userContextValue };
readingUserContext.user.isReadingRoom = true;

render(
<UserContext.Provider value={userContextValue}>
<WorkViewerWrapper manifestId="http://testing.com" />
<UserContext.Provider value={readingUserContext}>
<WorkViewerWrapper isWorkRestricted={true} manifestId="http://testing.com" />
</UserContext.Provider>
);

expect(screen.queryByText(readingRoomMessage)).not.toBeInTheDocument();
expect(
await screen.findByText(readingRoomMessage)
).toBeInTheDocument();

});

it("does not render an announcement when in the Reading Room and the Work is not restricted", async () => {
const readingUserContext = { ...userContextValue };
readingUserContext.user.isReadingRoom = true;

render(
<UserContext.Provider value={readingUserContext}>
<WorkViewerWrapper manifestId="http://testing.com" />
<WorkViewerWrapper isWorkRestricted={false} manifestId="http://testing.com" />
</UserContext.Provider>
);

expect(
await screen.findByText(
/You have access to Work because you are in the reading room/i
)
).toBeInTheDocument();
let el;
await waitFor(() => {
el = screen.queryByText(readingRoomMessage);
})
expect(el).toBeNull();
});
});
5 changes: 3 additions & 2 deletions components/Work/ViewerWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ export const CloverIIIF: React.ComponentType<{

interface WrapperProps {
manifestId: Work["iiif_manifest"];
isWorkRestricted?: boolean;
}

const WorkViewerWrapper: React.FC<WrapperProps> = ({ manifestId }) => {
const WorkViewerWrapper: React.FC<WrapperProps> = ({ manifestId, isWorkRestricted }) => {
const userAuth = React.useContext(UserContext);

const customTheme = {
Expand Down Expand Up @@ -67,7 +68,7 @@ const WorkViewerWrapper: React.FC<WrapperProps> = ({ manifestId }) => {
options={options}
/>
)}
{userAuth?.user?.isReadingRoom && (
{isWorkRestricted && userAuth?.user?.isReadingRoom && (
<Announcement>
<AnnouncementContent>
<IconInfo />
Expand Down
1 change: 1 addition & 0 deletions lib/dc-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ async function apiGetRequest<R>(
obj: ApiGetRequestParams
): Promise<R | undefined> {
const { url } = obj;

try {
const response = await axios({
url,
Expand Down
12 changes: 6 additions & 6 deletions pages/collections/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,15 @@ const Collection: NextPage = () => {
const id = router.query.id;
if (!id || Array.isArray(id)) return;
const data = await getCollection(id);

// This is not preferred, but auth is only respected client side
// so need this for items to display in Reading Room
if (!data) return router.push("/404");

setCollection(data);
}
router.isReady && getData();
}, [router.isReady, router.query.id]);
}, [router, router.isReady, router.query.id]);

/** Get dependant data */
useEffect(() => {
Expand Down Expand Up @@ -203,11 +208,6 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
const id = context?.params?.id;
const collection = await getCollection(id as string);

if (typeof collection === "undefined")
return {
notFound: true,
};

/** Add values to GTM's dataLayer object */
const dataLayer = buildDataLayer({
adminset: "",
Expand Down
22 changes: 14 additions & 8 deletions pages/items/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { buildWorkDataLayer } from "@/lib/ga/data-layer";
import { buildWorkOpenGraphData } from "@/lib/open-graph";
import { getIIIFResource } from "@/lib/dc-api";
import { loadItemStructuredData } from "@/lib/json-ld";
import { useRouter } from "next/router";
import useWorkAuth from "@/hooks/useWorkAuth";

interface WorkPageProps {
Expand All @@ -35,6 +36,7 @@ const WorkPage: NextPage<WorkPageProps> = ({ collectionWorkCounts, id }) => {
const [work, setWork] = useState<Work>();
const [manifest, setManifest] = useState<Manifest>();
const { isWorkRestricted } = useWorkAuth(work);
const router = useRouter();

const isReadingRoom = userAuthContext?.user?.isReadingRoom;
const related = work ? getWorkSliders(work) : [];
Expand All @@ -48,15 +50,21 @@ const WorkPage: NextPage<WorkPageProps> = ({ collectionWorkCounts, id }) => {

async function getData() {
const work = await getWork(id);
if (!work) return setIsLoading(false);
if (!work) {
// This is not preferred, but auth is only respected client side
// so need this for items to display in Reading Room
router.push("/404");

return setIsLoading(false);
}
setWork(work);
const manifest = await getIIIFResource<Manifest>(work.iiif_manifest);
setManifest(manifest);
setIsLoading(false);
}

getData();
}, [id]);
}, [id, router]);

return (
<>
Expand All @@ -83,7 +91,10 @@ const WorkPage: NextPage<WorkPageProps> = ({ collectionWorkCounts, id }) => {
<WorkProvider initialState={{ manifest: manifest, work: work }}>
<ErrorBoundary FallbackComponent={ErrorFallback}>
{work.iiif_manifest && (isReadingRoom || !isWorkRestricted) && (
<WorkViewerWrapper manifestId={work.iiif_manifest} />
<WorkViewerWrapper
manifestId={work.iiif_manifest}
isWorkRestricted={isWorkRestricted}
/>
)}
{work && !isReadingRoom && isWorkRestricted && (
<WorkRestrictedDisplay thumbnail={work.thumbnail} />
Expand Down Expand Up @@ -114,11 +125,6 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
const id = context?.params?.id;
const work = await getWork(id as string);

if (typeof work === "undefined")
return {
notFound: true,
};

const collectionWorkCounts = work?.collection
? await getCollectionWorkCounts(work?.collection.id)
: null;
Expand Down

0 comments on commit a95a37c

Please sign in to comment.