Skip to content

Commit

Permalink
Merge pull request #64 from mbland/enable-separate-backend
Browse files Browse the repository at this point in the history
Allow frontend dev server to access Tomcat backend
  • Loading branch information
mbland committed Dec 19, 2023
2 parents 5a22ec9 + 75858de commit 1327961
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 8 deletions.
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,47 @@ plugin:
- Configure the selected plugin to process the downloaded
`jacocoXmlTestReport.xml` file.

## Allow frontend dev server to access Tomcat backend

It's possible to develop the frontend code served by Vite and
the backend code served by Tomcat simultaneously.

### Start Tomcat

For now, this requires running a fresh build of the app in Docker or setting
up your own IntelliJ IDEA Run Configuration. I hope to have another solution
set up shortly that would require neither.

```sh
# Compile the current code into strcalc.war.
./gradlew build

# Serve the strcalc.war file via Docker
./bin/tomcat-docker.sh
```

This is enabled by the `CORSFilter` settings in the application's
[web.xml](./strcalc/src/main/webapp/WEB-INF/web.xml) file. See the comment
in that file for further references.

### Start frontend dev server

```sh
cd strcalc/src/main/frontend
STRCALC_BACKEND='http://localhost:8080/strcalc/' pnpm dev
```

### Start frontend preview server

In preview mode (`pnpm build && pnpm preview`), the STRCALC_BACKEND value
will not propagate to the compiled bundle. However, entering the
following in the browser console will enable the compiled version to
communciate with the backend:

```js
globalThis.STRCALC_BACKEND='http://localhost:8080/strcalc/'
```

## Adding large tests

Coming soon...
Expand Down
11 changes: 9 additions & 2 deletions strcalc/src/main/frontend/components/calculators.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,23 @@ import { postFormData } from './request'

export const DEFAULT_ENDPOINT = './add'

const defaultPost = async (data)=> postFormData(DEFAULT_ENDPOINT, data)
const backendUrl = () => globalThis.STRCALC_BACKEND ?
new URL(DEFAULT_ENDPOINT, globalThis.STRCALC_BACKEND).toString() :
DEFAULT_ENDPOINT

const backendCalculator = async (data)=> postFormData(backendUrl(), data)

const tempCalculator = async (data) => Promise.reject(new Error(
`Temporary in-browser calculator received: "${data.get('numbers')}"`
))

/**
* Collection of production String Calculator implementations
*
* Each implementation takes a FormData instance containing only a
* 'numbers' field as its single argument.
*/
export default {
'api': { label: 'Tomcat backend API (Java)', impl: defaultPost },
'api': { label: 'Tomcat backend API (Java)', impl: backendCalculator },
'browser': { label: 'In-browser (JavaScript)', impl: tempCalculator }
}
26 changes: 20 additions & 6 deletions strcalc/src/main/frontend/components/calculators.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,27 @@ describe('calculators', () => {

afterEach(() => { vi.unstubAllGlobals() })

test('defaultPost requests expected backend', async () => {
const data = setupData('2,2')
const fetchStub = setupFetchStub(JSON.stringify({ result: 5 }))
describe('defaultPost', () => {
test('posts same server by default', async () => {
const data = setupData('2,2')
const fetchStub = setupFetchStub(JSON.stringify({ result: 5 }))

await expect(calculators.api.impl(data)).resolves.toEqual({ result: 5 })
expect(fetchStub).toHaveBeenCalledWith(
DEFAULT_ENDPOINT, postOptions({ numbers: '2,2' }))
})

test('posts to globalThis.STRCALC_BACKEND', async () => {
const data = setupData('2,2')
const fetchStub = setupFetchStub(JSON.stringify({ result: 5 }))
vi.stubGlobal('STRCALC_BACKEND', 'http://localhost:8080/strcalc/')

await expect(calculators.api.impl(data)).resolves.toEqual({ result: 5 })
expect(fetchStub).toHaveBeenCalledWith(
DEFAULT_ENDPOINT, postOptions({ numbers: '2,2' }))
await expect(calculators.api.impl(data)).resolves.toEqual({ result: 5 })
expect(fetchStub).toHaveBeenCalledWith(
new URL(DEFAULT_ENDPOINT, 'http://localhost:8080/strcalc/').toString(),
postOptions({ numbers: '2,2' })
)
})
})

test('tempCalculator rejects with Error', async () => {
Expand Down
3 changes: 3 additions & 0 deletions strcalc/src/main/frontend/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export default defineConfig({
plugins: [
handlebarsPrecompiler({ helpers: ['components/helpers.js'] })
],
define: {
STRCALC_BACKEND: JSON.stringify(process.env.STRCALC_BACKEND)
},
build: {
outDir: buildDir('webapp'),
sourcemap: true
Expand Down
19 changes: 19 additions & 0 deletions strcalc/src/main/webapp/WEB-INF/web.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,23 @@
jakarta.enterprise.inject.spi.BeanManager
</resource-env-ref-type>
</resource-env-ref>

<!-- Set CORS headers to run frontend and backend separately.
- https://stackoverflow.com/a/18850438
- https://tomcat.apache.org/tomcat-10.1-doc/config/filter.html#CORS_Filter
- https://tomcat.apache.org/tomcat-10.1-doc/images/cors-flowchart.png
- https://tomcat.apache.org/tomcat-10.1-doc/api/org/apache/catalina/filters/CorsFilter.html
-->
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
<init-param>
<param-name>cors.allowed.origins</param-name>
<param-value>http://localhost:5173, http://localhost:4173</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

0 comments on commit 1327961

Please sign in to comment.