Skip to content

Refactor lectern-client Rest Module to work in Browser or on Server#247

Merged
joneubank merged 10 commits into
mainfrom
fix/client-for-browser
May 21, 2025
Merged

Refactor lectern-client Rest Module to work in Browser or on Server#247
joneubank merged 10 commits into
mainfrom
fix/client-for-browser

Conversation

@joneubank
Copy link
Copy Markdown
Contributor

@joneubank joneubank commented May 20, 2025

Description

This includes the final changes needed for the lectern-client to run inside a browser, while still running succesfully on the server.

This requires removing two dependencies that only work inside NodeJS (winston and node-fetch), and refactoring the modules that were using them. The client's rest module was re-written from scratch to provide functions to list all dictionaries on a server, and to fetch a single Dictionary either by ID or by Name and Version.

Future work is needed to also include a getDiff or getSchema/getField. All the POST/PUT endpoints that modify data will also require auth integration, and these have been left for future tasks.

This is a breaking change in that it changes which fetch functions were available in the client, but it should also unblock any browser integrations.

Server CORS

In addition to the client changes, the Lectern Server needed to have CORS configuration added so that requests from a browser would succeed. The server has the Express library updated to latest and the expressjs/cors middleware added. An environment variable is now provided that will allow configuration of the allowed CORS domains. By default, CORS will reject all domains, so no security changes for existing servers.

Fixes

Type of change

Please choose only the relevant options and remove the others.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration.

  • Server has unit tests added for CORS functionality
  • Client has been tested through REPL import of the built library and integration tests run against a local running Lectern Server to list and get dictionaries.

Future Work

Checklist:

You do not need to fullfill all requirements of this checklist, select all that apply:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules
  • I have checked my code and corrected any misspellings

Copy link
Copy Markdown
Contributor

@ciaranschutte ciaranschutte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

couple of comments. tests + validation are looking great.

Comment thread apps/server/package.json Outdated
@@ -22,11 +22,15 @@
"homepage": "https://github.com/overture-stack/lectern#readme",
"devDependencies": {
"@types/body-parser": "^1.19.5",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove types for unused body-parser package

Comment thread apps/server/package.json Outdated
"@types/chai-http": "^4.2.4",
"@types/cors": "^2.8.18",
"@types/errorhandler": "0.0.32",
"@types/express": "^4.17.21",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

upgraded express package but not it's types

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This helped me spot an underlying type issue, thank you.

"dependencies": {
"@overture-stack/lectern-dictionary": "workspace:^",
"@overture-stack/lectern-validation": "workspace:^",
"axios": "^1.7.2",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment.

web standard fetch is available in node
axios is overkill
aligns with removing things we don't need (body-parser removes in preference for native express api)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetch in any of its flavors doesn't include cancelling (you'd have to instantiate an AbortController separately), which is something we've been meaning to start implementing consistently.

does that add any nuance to this choice?

Comment on lines +32 to +35
// export const fetchDiffAndAnalyze = async (serviceUrl: string, name: string, fromVersion: string, toVersion: string) => {
// // const changes = await restClient.fetchDiff(serviceUrl, name, fromVersion, toVersion);
// return analyzeChanges(changes);
// };
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete or add comment explaining why code is commented out

Comment thread packages/client/README.md Outdated
const allAvailableDictionariesResult = await lectern.rest.listDictionaries(lecternUrl);
const filteredByNameDictionariesResult = await lectern.rest.listDictionaries(lecternUrl, {name: dictionaryName});

const exampleDicionaryResult = await lectern.rest.fetchDiff(lecternUrl, {name: dictionaryName, version: dictionaryVersion});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is fetchDiff removed? description of PR says

"Future work is needed to also include a getDiff or getSchema/getField. All the POST/PUT endpoints that modify data will also require auth integration, and these have been left for future tasks."

Comment thread apps/server/.env.example
# AUTH_ENABLED=false
# EGO_API=
# SCOPE=
# # CORS allowed origins can be a comma separated list of the allowed domains.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this parse spacing? e.g. a,b,c vs a, b, c

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, this takes whitespace as part of the value. very simple implementation

Comment thread apps/server/README.md
Comment on lines +39 to +43
pnpm nx build @overture-stack/lectern-server
```
or
```shell
pnpm build
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the difference, if any? otherwise, line 43 should suffice as "lower complexity" choice

Comment thread apps/server/README.md
Comment on lines +49 to +53
pnpm nx start @overture-stack/lectern-server
```
or
```shell
pnpm start
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here. difference? otherwise, simpler is best

Comment thread apps/server/README.md
| -------------------- | ------------------------------ | ------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| OPENAPI_PATH | No | String | `/api-docs` | Path to Swagger UI with API documentation. |
| PORT | No | Number | `3000` | Port Lectern Server API will listen to. |
| CORS_ALLOWED_ORIGINS | No | String[] | - | List of domains that will be allowed by CORS. Multiple domains can be listed, separated by commas. Example: `http://localhost:5173,https://example.com` |
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, looks like this is the answer to my earlier question. perhaps examples would be useful in the .env.example? (unless the intent was to create a .env.template instead)

Suggested change
| CORS_ALLOWED_ORIGINS | No | String[] | - | List of domains that will be allowed by CORS. Multiple domains can be listed, separated by commas. Example: `http://localhost:5173,https://example.com` |
| CORS_ALLOWED_ORIGINS | No | String[] | - | List of domains that will be allowed by CORS. Multiple domains can be listed, separated by commas. No spaces. Example: `http://localhost:5173,https://example.com` |

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add the trim thing instead.

Comment thread apps/server/src/config/appConfig.ts Outdated
};
return res.status(200).send(resBody);
res.status(200).send(resBody);
return;
Copy link
Copy Markdown
Member

@justincorrigible justincorrigible May 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

naive question: return is implicitly undefined, so it must be to end the condition. what's the purpose of putting it separately from the response? surely return res... has the exact same effect without looking unnecessarily verbose, and following a well established expressjs pattern.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updated version of Express's request handlers have a return type that doesn't allow returning the response function, so the existing returns were breaking build. I add the explicit return by habit to end the function after sending the response to prevent accidentally modifying a response that's already been sent (which causes exceptions in express).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ugh Express! le why you le break? 😞
it was a nice pattern! are you sure it's not just their TS implementation that's the problem?

Co-authored-by: Anders Richardsson <2107110+justincorrigible@users.noreply.github.com>
@joneubank joneubank force-pushed the fix/client-for-browser branch from c061713 to b714db8 Compare May 21, 2025 17:43
@joneubank joneubank merged commit 9b46a9c into main May 21, 2025
2 checks passed
@joneubank joneubank deleted the fix/client-for-browser branch May 21, 2025 18:51
justincorrigible added a commit to Pan-Canadian-Genome-Library/dictionary-manager that referenced this pull request May 21, 2025
…verture-stack#247)

* Enable CORS for server by configuration

* Refactor client rest module to work in browser or server

* Update client README for latest Rest module function usage

* Remove distracting comments from test

* Remove unused code and references to old dependencies

* Upgrade @types packages and update affected code

* Remove vestigial change analysis code imported from legacy client

* Organize imports, format code example in readme

* Trim cors allowed origins values

Co-authored-by: Anders Richardsson <2107110+justincorrigible@users.noreply.github.com>

* Remove tests associated with removed code

---------

Co-authored-by: Anders Richardsson <2107110+justincorrigible@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants