├── src ├── index.ts ├── parser │ ├── responseParser.ts │ ├── index.ts │ ├── defaultParser.ts │ └── plainTextParser.ts └── restservice │ ├── index.ts │ ├── dataRequestResult.ts │ ├── requestOptions.ts │ └── piRestService.ts ├── .prettierrc.json ├── .github ├── dependabot.yml └── workflows │ ├── npm-test.yml │ ├── github-pages.yml │ └── npm-publish.yml ├── .gitignore ├── tsconfig.json ├── README.md ├── eslint.config.js ├── vite.config.ts ├── test └── unit │ ├── mock │ └── locations.json │ └── piRestService.spec.ts ├── developer.md ├── LICENSE └── package.json /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './restservice/index.js' 2 | export * from './parser/index.js' 3 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "endOfLine": "auto" 5 | } 6 | -------------------------------------------------------------------------------- /src/parser/responseParser.ts: -------------------------------------------------------------------------------- 1 | export interface ResponseParser { 2 | parse(response: Response): Promise; 3 | } 4 | -------------------------------------------------------------------------------- /src/restservice/index.ts: -------------------------------------------------------------------------------- 1 | export * from './dataRequestResult.js' 2 | export * from './piRestService.js' 3 | export * from './requestOptions.js' 4 | -------------------------------------------------------------------------------- /src/parser/index.ts: -------------------------------------------------------------------------------- 1 | export * from './defaultParser.js' 2 | export * from './plainTextParser.js' 3 | export * from './responseParser.js' 4 | export * from './plainTextParser.js' 5 | -------------------------------------------------------------------------------- /src/restservice/dataRequestResult.ts: -------------------------------------------------------------------------------- 1 | export default interface DataRequestResult { 2 | responseCode: number; 3 | contentType: string | null; 4 | data: T; 5 | 6 | } 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | open-pull-requests-limit: 1 8 | 9 | -------------------------------------------------------------------------------- /src/parser/defaultParser.ts: -------------------------------------------------------------------------------- 1 | import {ResponseParser} from "../parser/responseParser.js"; 2 | 3 | export class DefaultParser implements ResponseParser { 4 | async parse(response: Response): Promise { 5 | return await response.json() as T 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/parser/plainTextParser.ts: -------------------------------------------------------------------------------- 1 | import {ResponseParser} from "../parser/responseParser.js"; 2 | 3 | export class PlainTextParser implements ResponseParser { 4 | async parse(response: Response): Promise { 5 | const text = await response.text() 6 | return text as never; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | /lib 5 | /doc 6 | /coverage 7 | /reports 8 | 9 | # local env files 10 | .env.local 11 | .env.*.local 12 | 13 | # Log files 14 | npm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | /test/unit/coverage/ 25 | -------------------------------------------------------------------------------- /.github/workflows/npm-test.yml: -------------------------------------------------------------------------------- 1 | name: Run npm test & npm lint on Pull Requests 2 | 3 | on: 4 | - pull_request 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v5 11 | - uses: actions/setup-node@v6 12 | with: 13 | node-version: 22 14 | - run: npm ci 15 | 16 | - name: Run unit tests 17 | run: npm run test:unit 18 | 19 | - name: Run linter 20 | run: npm run lint 21 | 22 | -------------------------------------------------------------------------------- /src/restservice/requestOptions.ts: -------------------------------------------------------------------------------- 1 | export class RequestOptions { 2 | private _mode = "cors"; 3 | private _relativeUrl = true; 4 | 5 | 6 | get relativeUrl(): boolean { 7 | return this._relativeUrl; 8 | } 9 | 10 | set relativeUrl(value) { 11 | this._relativeUrl = value; 12 | } 13 | 14 | get mode(): string { 15 | return this._mode; 16 | } 17 | 18 | set mode(value: string) { 19 | this._mode = value; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "esnext", 5 | "jsx": "preserve", 6 | "resolveJsonModule": true, 7 | "lib": ["ES2022", "dom"], 8 | "types": ["vite/client", "@types/node"], 9 | 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "isolatedModules": true, 13 | "moduleDetection": "force", 14 | "noEmit": true, 15 | 16 | "strict": true 17 | }, 18 | "include": ["src/**/*.ts", "test/**/*.ts", "vite.config.ts"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fews-web-oc-utils 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | ### Compiles and minifies for production 8 | ``` 9 | npm run build 10 | ``` 11 | Build only esm bundle 12 | ``` 13 | npm run build:esm 14 | ``` 15 | Build only cjs bundle 16 | ``` 17 | npm run build:cjs 18 | ``` 19 | ### Lints and fixes files 20 | ``` 21 | npm run lint 22 | ``` 23 | 24 | ### Run tests 25 | Get the coverage report 26 | ``` 27 | npm run test 28 | ``` 29 | Run only unit tests 30 | ``` 31 | npm run test:unit 32 | ``` 33 | Run only e2e tests 34 | ``` 35 | npm run test:e2e 36 | ``` 37 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import eslint from '@eslint/js' 2 | import { defineConfig, globalIgnores } from 'eslint/config' 3 | import globals from 'globals' 4 | import tseslint from 'typescript-eslint' 5 | import eslintConfigPrettier from 'eslint-config-prettier/flat' 6 | 7 | export default defineConfig( 8 | globalIgnores(['coverage/', 'dist/', 'doc/']), 9 | eslint.configs.recommended, 10 | tseslint.configs.recommended, 11 | eslintConfigPrettier, 12 | { 13 | languageOptions: { 14 | globals: globals.browser, 15 | parserOptions: { 16 | projectService: { allowDefaultProject: ['*.js'] }, 17 | }, 18 | }, 19 | }, 20 | ) 21 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path' 2 | 3 | import rollupPluginTypescript from '@rollup/plugin-typescript' 4 | import { defineConfig } from 'vite' 5 | 6 | function resolveRelativePath(relative: string): string { 7 | return resolve(__dirname, relative) 8 | } 9 | 10 | export default defineConfig({ 11 | build: { 12 | lib: { 13 | entry: resolveRelativePath('src/index.ts'), 14 | formats: ['es'], 15 | name: 'fews-web-oc-utils', 16 | fileName: 'fews-web-oc-utils' 17 | }, 18 | rollupOptions: { 19 | plugins: [ 20 | rollupPluginTypescript({ 21 | allowImportingTsExtensions: false, 22 | declaration: true, 23 | declarationDir: resolveRelativePath('dist'), 24 | rootDir: resolveRelativePath('src') 25 | }) 26 | ] 27 | } 28 | } 29 | }) 30 | -------------------------------------------------------------------------------- /test/unit/mock/locations.json: -------------------------------------------------------------------------------- 1 | { 2 | "locations": [{ 3 | "locationId": "delfzijl", 4 | "shortName": "Delfzijl", 5 | "lat": "53.33000183105469", 6 | "lon": "6.929999828338623" 7 | }, { 8 | "locationId": "den_helder", 9 | "shortName": "Den Helder", 10 | "lat": "52.970001220703125", 11 | "lon": "4.75" 12 | }, { 13 | "locationId": "dordrecht", 14 | "shortName": "Dordrecht", 15 | "lat": "51.81999969482422", 16 | "lon": "4.670000076293945" 17 | }, { 18 | "locationId": "harlingen", 19 | "shortName": "Harlingen", 20 | "lat": "53.18000030517578", 21 | "lon": "5.409999847412109" 22 | }, { 23 | "locationId": "hoekvanholland", 24 | "shortName": "Hoek van Holland", 25 | "lat": "51.97999954223633", 26 | "lon": "4.119999885559082" 27 | }, { 28 | "locationId": "vlissingen", 29 | "shortName": "Vlissingen", 30 | "lat": "51.439998626708984", 31 | "lon": "3.5999999046325684" 32 | }] 33 | } -------------------------------------------------------------------------------- /developer.md: -------------------------------------------------------------------------------- 1 | ## Creating a release 2 | 3 | Before creating a release make sure that all changes are merged to the main branch. 4 | 5 | 1. Update the version number to X.Y.Z 6 | 7 | ``` 8 | git checkout main 9 | git pull 10 | npm version X.Y.Z 11 | ``` 12 | The last command updates the version in `package.json` and `package-lock.json` and creates a git tag `vX.Y.Z` 13 | 14 | 2. Push the changes to Github 15 | 16 | ``` 17 | git push 18 | git push origin vX.Y.Z 19 | ``` 20 | 21 | 3. Create a release on Github 22 | 23 | Creating a release on Github is a manual step. For more information see: https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository 24 | Create the release from the existing tag we just created. For the title of the release use `vX.Y.Z`. Use the `Generate releasenotes` button to list all pull request that are included in this release. When the release is created the Github workflow '.github/workflows/npm-publish' will run automatically to deploy the new version to https://www.npmjs.com/package/@deltares/fews-web-oc-utils -------------------------------------------------------------------------------- /.github/workflows/github-pages.yml: -------------------------------------------------------------------------------- 1 | name: Github Pages 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | build-pages: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v5 12 | - uses: actions/setup-node@v6 13 | with: 14 | node-version: 22 15 | - run: npm i 16 | - run: npx typedoc src/index.ts 17 | - uses: actions/upload-artifact@v5 18 | with: 19 | name: pages 20 | path: docs 21 | 22 | deploy-pages: 23 | needs: build-pages 24 | environment: 25 | name: github-pages 26 | url: ${{ steps.deployment.outputs.page_url }} 27 | permissions: 28 | contents: read 29 | pages: write 30 | id-token: write 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v5 34 | - uses: actions/download-artifact@v6 35 | with: 36 | name: pages 37 | path: docs 38 | - uses: actions/configure-pages@v5 39 | - uses: actions/upload-pages-artifact@v4 40 | with: 41 | path: 'docs' 42 | - uses: actions/deploy-pages@v4 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Deltares 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | permissions: 7 | id-token: write # Required for OIDC 8 | contents: read 9 | 10 | on: 11 | release: 12 | types: [created] 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v5 19 | - uses: actions/setup-node@v6 20 | with: 21 | node-version: 22 22 | - run: npm ci 23 | - run: npm run test:unit 24 | 25 | publish-npm: 26 | needs: build 27 | runs-on: ubuntu-latest 28 | steps: 29 | - uses: actions/checkout@v5 30 | - uses: actions/setup-node@v6 31 | with: 32 | node-version: 22 33 | - name: Update npm # 11.5.0 required for OIDC authentication 34 | run: npm install -g npm@latest 35 | - run: npm ci 36 | - run: npm run build 37 | - run: npm publish --access public --verbose 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@deltares/fews-web-oc-utils", 3 | "version": "2.0.1", 4 | "description": "Util Library for common Web OC functionality", 5 | "author": { 6 | "name": "Werner Kramer", 7 | "email": "werner.kramer@deltares.nl" 8 | }, 9 | "keywords": [ 10 | "FEWS", 11 | "PI REST Service", 12 | "Web Operator Client", 13 | "Web OC" 14 | ], 15 | "license": "MIT", 16 | "private": false, 17 | "type": "module", 18 | "module": "./dist/fews-web-oc-utils.js", 19 | "exports": { 20 | ".": { 21 | "import": "./dist/fews-web-oc-utils.js", 22 | "types": "./dist/index.d.ts" 23 | } 24 | }, 25 | "scripts": { 26 | "build": "tsc && vite build", 27 | "doc": "typedoc --out doc src", 28 | "lint": "eslint", 29 | "lint:fix": "eslint --fix", 30 | "test:unit": "vitest run test/unit", 31 | "test:e2e": "vitest run test/e2e", 32 | "test": "vitest", 33 | "sonar": "sonar-scanner -Dsonar.host.url=$SONAR_URL -Dsonar.login=$SONAR_TOKEN -Dsonar.projectKey=$SONAR_KEY -Dsonar.projectName='Delft-FEWS Web OC Utils'" 34 | }, 35 | "devDependencies": { 36 | "@rollup/plugin-typescript": "^12.3.0", 37 | "@types/node": "^22.19.0", 38 | "eslint": "^9.39.1", 39 | "eslint-config-prettier": "^10.1.8", 40 | "fetch-mock": "^12.6.0", 41 | "json-schema-to-typescript": "^15.0.4", 42 | "sonarqube-scanner": "^4.3.2", 43 | "tslib": "^2.8.1", 44 | "typedoc": "^0.28.14", 45 | "typescript": "^5.9.3", 46 | "typescript-eslint": "^8.46.4", 47 | "vite": "^7.2.2", 48 | "vitest": "^4.0.6" 49 | }, 50 | "engines": { 51 | "node": ">=22.0.0" 52 | }, 53 | "files": [ 54 | "dist/*" 55 | ], 56 | "repository": { 57 | "type": "git", 58 | "url": "git+https://github.com/Deltares/fews-web-oc-utils.git" 59 | }, 60 | "bugs": { 61 | "url": "https://github.com/Deltares/fews-web-oc-utils/issues" 62 | }, 63 | "publishConfig": { 64 | "access": "public", 65 | "registry": "https://registry.npmjs.org" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/restservice/piRestService.ts: -------------------------------------------------------------------------------- 1 | import DataRequestResult from "./dataRequestResult.js"; 2 | import {RequestOptions} from "./requestOptions.js"; 3 | import {ResponseParser} from "../parser/responseParser.js"; 4 | import {DefaultParser} from "../parser/defaultParser.js"; 5 | 6 | export interface TransformRequestFunction { 7 | (request: Request): Promise; 8 | } 9 | 10 | export class PiRestService { 11 | private readonly webserviceUrl: string; 12 | private transformRequest: TransformRequestFunction; 13 | 14 | constructor(webserviceUrl: string, transformRequestFn?: TransformRequestFunction) { 15 | this.webserviceUrl = webserviceUrl; 16 | if (transformRequestFn !== undefined) { 17 | this.transformRequest = transformRequestFn 18 | } else { 19 | async function transformRequestFn(request: Request): Promise { 20 | return request 21 | } 22 | this.transformRequest = transformRequestFn 23 | } 24 | } 25 | 26 | public async getDataWithParser(url: string, requestOption: RequestOptions, parser: ResponseParser): Promise> { 27 | const requestUrl = requestOption.relativeUrl ? this.webserviceUrl + url : url; 28 | const dataRequestResult = {} as DataRequestResult; 29 | const requestParameters = {} as RequestInit; 30 | requestParameters.method = "GET"; 31 | const request = new Request(requestUrl, requestParameters); 32 | const res = await fetch(await this.transformRequest(request)); 33 | if (!res.ok) throw new Error('Fetch Error', { cause: res }) 34 | return await this.processResponse(dataRequestResult, res, requestUrl, parser); 35 | } 36 | 37 | public async postDataWithParser(url: string, requestOption: RequestOptions, parser: ResponseParser, body: BodyInit, headers: HeadersInit): Promise> { 38 | const requestUrl = requestOption.relativeUrl ? this.webserviceUrl + url : url; 39 | const dataRequestResult = {} as DataRequestResult; 40 | const requestParameters = {} as RequestInit; 41 | requestParameters.method = "POST"; 42 | requestParameters.body = body; 43 | requestParameters.headers = headers; 44 | const request = new Request(requestUrl, requestParameters); 45 | const res = await fetch(await this.transformRequest(request)); 46 | if (!res.ok) throw new Error('Fetch Error', { cause: res }) 47 | return await this.processResponse(dataRequestResult, res, requestUrl, parser); 48 | } 49 | 50 | public async getData(url: string): Promise> { 51 | const requestOption = new RequestOptions() 52 | requestOption.relativeUrl = !url.startsWith("http") 53 | return this.getDataWithParser(url, requestOption, new DefaultParser()); 54 | } 55 | 56 | public async postData(url: string, body: BodyInit, headers: HeadersInit = { "Content-Type": "application/json" }): Promise> { 57 | const requestOption = new RequestOptions() 58 | requestOption.relativeUrl = !url.startsWith("http") 59 | return this.postDataWithParser(url, requestOption, new DefaultParser(), body, headers); 60 | } 61 | 62 | private async processResponse(dataRequestResult: DataRequestResult, res: Response, url: string, parser: ResponseParser): Promise> { 63 | dataRequestResult.responseCode = res.status; 64 | dataRequestResult.contentType = res.headers.get('content-type') 65 | try { 66 | dataRequestResult.data = await parser.parse(res); 67 | } catch (e: unknown) { 68 | throw new Error(`Parse Error for response ${url}.`, { cause: e }); 69 | } 70 | return dataRequestResult; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/unit/piRestService.spec.ts: -------------------------------------------------------------------------------- 1 | import fetchMock from 'fetch-mock' 2 | import { afterAll, beforeAll, describe, expect, it } from 'vitest' 3 | 4 | import { PiRestService } from '../../src/restservice/piRestService.js' 5 | import { PlainTextParser } from '../../src/parser/plainTextParser.js' 6 | import { RequestOptions } from '../../src/restservice/requestOptions.js' 7 | 8 | import expectedLocations from './mock/locations.json' 9 | 10 | async function transformRequest(request: Request): Promise { 11 | const requestInit: RequestInit = { 12 | // Only some of the properties of RequestInit are used by fetch-mock, such as 'headers'. 13 | headers: { 'Content-Type': 'application/json' }, 14 | } 15 | const newRequest = new Request(request, requestInit) 16 | return newRequest 17 | } 18 | 19 | async function transformRequestWithToken(request: Request): Promise { 20 | const requestInit: RequestInit = { 21 | // Only some of the properties of RequestInit are used by fetch-mock, such as 'headers'. 22 | headers: { 23 | 'Content-Type': 'application/json', 24 | Authorization: 'Bearer testtoken', 25 | }, 26 | } 27 | const newRequest = new Request(request, requestInit) 28 | return newRequest 29 | } 30 | 31 | const baseUrl = 'https://mock.dev/fewswebservices/' 32 | 33 | describe('pi rest service: GET', function () { 34 | beforeAll(() => fetchMock.mockGlobal()) 35 | 36 | afterAll(() => { 37 | fetchMock.hardReset() 38 | }) 39 | 40 | it('Get Locations', async function () { 41 | fetchMock.route( 42 | 'https://mock.dev/fewswebservices/rest/fewspiservice/v1/locations?documentFormat=PI_JSON', 43 | { 44 | status: 200, 45 | body: JSON.stringify(expectedLocations), 46 | }, 47 | ) 48 | const provider = new PiRestService(baseUrl, transformRequestWithToken) 49 | const res = await provider.getData( 50 | 'rest/fewspiservice/v1/locations?documentFormat=PI_JSON', 51 | ) 52 | expect(res.data).not.toBeNull() 53 | expect(res.data).toStrictEqual(expectedLocations) 54 | expect(res.responseCode).toStrictEqual(200) 55 | expect( 56 | fetchMock.callHistory.lastCall()?.request?.headers.get('Content-Type'), 57 | ).toBe('application/json') 58 | const transformProvider = new PiRestService(baseUrl, transformRequest) 59 | const resWithTransformedRequest = await transformProvider.getData( 60 | 'rest/fewspiservice/v1/locations?documentFormat=PI_JSON', 61 | ) 62 | expect(resWithTransformedRequest.data).not.toBeNull() 63 | expect( 64 | fetchMock.callHistory.lastCall()?.request?.headers.get('Content-Type'), 65 | ).toBe('application/json') 66 | expect(resWithTransformedRequest.data).toStrictEqual(expectedLocations) 67 | }) 68 | it('Get Locations Not Found', async function () { 69 | const mockReponse = { 70 | status: 400, 71 | body: JSON.stringify('not found'), 72 | } 73 | fetchMock.route( 74 | 'https://mock.dev/fewswebservices/rest/fewspiservice/v1/locations', 75 | mockReponse, 76 | ) 77 | const provider = new PiRestService(baseUrl) 78 | try { 79 | const res = provider.getData( 80 | 'rest/fewspiservice/v1/locations', 81 | ) 82 | await expect(res).rejects.toThrow('Fetch Error') 83 | } catch (error) { 84 | expect(error).toBeInstanceOf(Error) 85 | if (error instanceof Error) { 86 | expect(error.message).toBe('Fetch Error') 87 | 88 | // The cause will be a Response 89 | const response = error.cause as Response 90 | 91 | expect(response).toBeInstanceOf(Response) 92 | expect(response.status).toBe(400) 93 | const text = await response.text() 94 | expect(text).toBe('"not found"') 95 | } 96 | } 97 | }) 98 | it('Get Locations Invalid JSON response', async function () { 99 | fetchMock.route( 100 | 'https://mock.dev/fewswebservices/rest/fewspiservice/v1/locations?invalid', 101 | { 102 | status: 200, 103 | body: '{', 104 | }, 105 | ) 106 | const provider = new PiRestService(baseUrl) 107 | const res = provider.getData( 108 | 'rest/fewspiservice/v1/locations?invalid', 109 | ) 110 | await expect(res).rejects.toThrow( 111 | 'Parse Error for response https://mock.dev/fewswebservices/rest/fewspiservice/v1/locations?invalid', 112 | ) 113 | }) 114 | }) 115 | 116 | describe('pi rest service json body: POST', function () { 117 | beforeAll(() => { 118 | fetchMock.mockGlobal() 119 | 120 | fetchMock.post( 121 | 'https://mock.dev/fewswebservices/rest/fewspiservice/v1/timeseries/edit', 122 | { 123 | status: 200, 124 | body: JSON.stringify({ responseCode: 200 }), 125 | }, 126 | ) 127 | }) 128 | 129 | afterAll(() => { 130 | fetchMock.hardReset() 131 | }) 132 | 133 | it('Post timeseries/edit', async function () { 134 | const provider = new PiRestService(baseUrl) 135 | const res = await provider.postData( 136 | 'rest/fewspiservice/v1/timeseries/edit', 137 | JSON.stringify({ test: 'test' }), 138 | ) 139 | expect(res.data).not.toBeNull() 140 | expect(res.data).toStrictEqual({ responseCode: 200 }) 141 | // check if headers are equal to default 142 | expect( 143 | fetchMock.callHistory.lastCall()?.request?.headers.get('Content-Type'), 144 | ).toBe('application/json') 145 | }) 146 | 147 | it('Post timeseries/edit with given headers', async function () { 148 | const provider = new PiRestService(baseUrl) 149 | const headers = { 150 | 'Content-Type': 'application/ld+json', 151 | } 152 | const res = await provider.postData( 153 | 'rest/fewspiservice/v1/timeseries/edit', 154 | JSON.stringify({ test: 'test' }), 155 | headers, 156 | ) 157 | expect(res.data).not.toBeNull() 158 | expect(res.data).toStrictEqual({ responseCode: 200 }) 159 | expect( 160 | fetchMock.callHistory.lastCall()?.request?.headers.get('Content-Type'), 161 | ).toBe('application/ld+json') 162 | }) 163 | }) 164 | 165 | describe('pi rest service json urlencoded: POST', function () { 166 | beforeAll(() => { 167 | fetchMock.mockGlobal() 168 | }) 169 | 170 | afterAll(() => { 171 | fetchMock.hardReset() 172 | }) 173 | 174 | it('Post timeseries url encoded json post with text parser', async function () { 175 | fetchMock.post( 176 | 'https://mock.dev/fewswebservices/rest/fewspiservice/v1/timeseries', 177 | { 178 | status: 200, 179 | body: '{}', 180 | }, 181 | ) 182 | const provider = new PiRestService(baseUrl) 183 | const headers = { 184 | 'Content-Type': 'application/xml', 185 | } 186 | const requestOption = new RequestOptions() 187 | requestOption.relativeUrl = false 188 | const res = await provider.postDataWithParser( 189 | 'https://mock.dev/fewswebservices/rest/fewspiservice/v1/timeseries', 190 | requestOption, 191 | new PlainTextParser(), 192 | JSON.stringify({ test: 'test' }), 193 | headers, 194 | ) 195 | expect(res.data).not.toBeNull() 196 | expect(res.data).toStrictEqual('{}') 197 | expect(res.responseCode).toBe(200) 198 | expect( 199 | fetchMock.callHistory.lastCall()?.request?.headers.get('Content-Type'), 200 | ).toBe('application/xml') 201 | }) 202 | }) 203 | --------------------------------------------------------------------------------