Skip to content

Commit 6b1f4a0

Browse files
committed
Recover corrupted jsons by supporting native JS encoding when decoding document attachments.
1 parent fc8dc1e commit 6b1f4a0

File tree

4 files changed

+38
-45
lines changed

4 files changed

+38
-45
lines changed

icc-api/api/XHR.ts

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { ua2b64 } from '../model/ModelHelper'
2-
import { NoAuthService } from '../../icc-x-api/auth/NoAuthService'
3-
import { AuthService } from '../../icc-x-api/auth/AuthService'
1+
import {ua2b64, ua2string} from '../model/ModelHelper'
2+
import {NoAuthService} from '../../icc-x-api/auth/NoAuthService'
3+
import {AuthService} from '../../icc-x-api/auth/AuthService'
4+
import {ua2utf8} from "../../icc-x-api"
45

56
export namespace XHR {
67
export class Header {
@@ -49,12 +50,12 @@ export namespace XHR {
4950
fetchImpl: (input: RequestInfo, init?: RequestInit) => Promise<Response> = typeof window !== 'undefined'
5051
? window.fetch
5152
: typeof self !== 'undefined'
52-
? self.fetch
53-
: fetch
53+
? self.fetch
54+
: fetch
5455
): Promise<Response> {
5556
return new Promise((resolve, reject) => {
5657
// Set timeout timer
57-
let timer = setTimeout(() => reject({ message: 'Request timed out', status: 'Request timed out' }), timeout)
58+
let timer = setTimeout(() => reject({message: 'Request timed out', status: 'Request timed out'}), timeout)
5859
fetchImpl(url, init)
5960
.then((response) => {
6061
clearTimeout(timer)
@@ -75,11 +76,12 @@ export namespace XHR {
7576
fetchImpl: (input: RequestInfo, init?: RequestInit) => Promise<Response> = typeof window !== 'undefined'
7677
? window.fetch
7778
: typeof self !== 'undefined'
78-
? self.fetch
79-
: fetch,
79+
? self.fetch
80+
: fetch,
8081
contentTypeOverride?: 'application/json' | 'text/plain' | 'application/octet-stream',
8182
headerProvider: AuthService = new NoAuthService(),
82-
minimumAuthenticationClass: number | undefined = undefined
83+
minimumAuthenticationClass: number | undefined = undefined,
84+
tryHardToParseJson: boolean = false
8385
): Promise<Data> {
8486
const authHeaders = await headerProvider.getAuthHeaders(minimumAuthenticationClass)
8587
const contentType = headers && headers.find((it) => (it.header ? it.header.toLowerCase() === 'content-type' : false))
@@ -104,18 +106,18 @@ export namespace XHR {
104106
acc[h.header] = h.data
105107
return acc
106108
},
107-
{ 'X-Requested-With': 'XMLHttpRequest' }
109+
{'X-Requested-With': 'XMLHttpRequest'}
108110
),
109111
},
110112
method === 'POST' || method === 'PUT'
111113
? {
112-
body:
113-
!contentType || contentType.data === 'application/json'
114-
? JSON.stringify(data, (k, v) => {
115-
return v instanceof ArrayBuffer || v instanceof Uint8Array ? ua2b64(v) : v
116-
})
117-
: data,
118-
}
114+
body:
115+
!contentType || contentType.data === 'application/json'
116+
? JSON.stringify(data, (k, v) => {
117+
return v instanceof ArrayBuffer || v instanceof Uint8Array ? ua2b64(v) : v
118+
})
119+
: data,
120+
}
119121
: {}
120122
),
121123
timeout,
@@ -132,24 +134,32 @@ export namespace XHR {
132134
fetchImpl,
133135
contentTypeOverride,
134136
headerProvider,
135-
requiredAuthLevelHeader ? parseInt(requiredAuthLevelHeader) : undefined
137+
requiredAuthLevelHeader ? parseInt(requiredAuthLevelHeader) : undefined,
138+
tryHardToParseJson
136139
)
137140
} else if (response.status >= 400) {
138141
const error: {
139142
error: string
140143
message: string
141144
status: number
142-
} = { error: response.statusText, message: await response.text(), status: response.status }
145+
} = {error: response.statusText, message: await response.text(), status: response.status}
143146
console.warn(`XHR Error: ${method} ${url} - ${error.status} - ${error.error}`, error.message)
144147
throw new XHRError(url, error.message, error.status, error.error, response.headers)
145148
} else {
146149
const ct = contentTypeOverride || response.headers.get('content-type') || 'text/plain'
147150
return (
148151
ct.startsWith('application/json')
149-
? response.json()
152+
? tryHardToParseJson ? response.arrayBuffer().then(async (ab) => {
153+
try {
154+
return JSON.parse(ua2utf8(ab))
155+
} catch (e) {
156+
console.warn('Error parsing JSON fallback on ua2string', e)
157+
return JSON.parse(ua2string(ab).replace(/[\u0000-\u001F\u007F-\u009F]/g, ""))
158+
}
159+
}) : response.json()
150160
: ct.startsWith('application/xml') || ct.startsWith('text/')
151-
? response.text()
152-
: response.arrayBuffer()
161+
? response.text()
162+
: response.arrayBuffer()
153163
).then((d) => new Data(response.status, ct, d))
154164
}
155165
})

icc-x-api/icc-document-x-api.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -741,12 +741,12 @@ export class IccDocumentXApi extends IccDocumentApi implements EncryptedEntityXA
741741
//prettier-ignore
742742
getMainAttachmentAs(documentId: string, returnType: "text/plain"): Promise<string>
743743
//prettier-ignore
744-
getMainAttachmentAs(documentId: string, returnType: "application/json"): Promise<any>
744+
getMainAttachmentAs(documentId: string, returnType: "application/json", tryHardToParseJson?: boolean): Promise<any>
745745
//prettier-ignore
746-
getMainAttachmentAs(documentId: string, returnType: 'application/octet-stream' | 'text/plain' | 'application/json'): Promise<any>
747-
async getMainAttachmentAs(documentId: string, returnType: 'application/octet-stream' | 'text/plain' | 'application/json'): Promise<any> {
746+
getMainAttachmentAs(documentId: string, returnType: 'application/octet-stream' | 'text/plain' | 'application/json', tryHardToParseJson?: boolean): Promise<any>
747+
async getMainAttachmentAs(documentId: string, returnType: 'application/octet-stream' | 'text/plain' | 'application/json', tryHardToParseJson: boolean = false): Promise<any> {
748748
const url = this.host + `/document/${documentId}/attachment` + '?ts=' + new Date().getTime()
749-
return XHR.sendCommand('GET', url, await this.headers, null, this.fetchImpl, returnType, this.authenticationProvider.getAuthService())
749+
return XHR.sendCommand('GET', url, await this.headers, null, this.fetchImpl, returnType, this.authenticationProvider.getAuthService(), undefined, returnType == 'application/json' && tryHardToParseJson)
750750
.then((doc) => doc.body)
751751
.catch((err) => this.handleError(err))
752752
}

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@icure/api",
3-
"version": "8.2.3",
3+
"version": "8.2.7",
44
"description": "Typescript version of iCure standalone API client",
55
"types": "dist/index.d.ts",
66
"dependencies": {
@@ -60,7 +60,7 @@
6060
"build": "tsc",
6161
"testwp": "echo 'Test webpack build' && yarn webpack --config buildconfig/webpack.config.js || rm -rf dist && rm -rf buildconfig/dist-wp",
6262
"prepare": "rimraf dist && npm run build && jq '{name:.name, version:.version, description:.description, main:\"index.js\", types:\"index.d.ts\", dependencies:.dependencies, author:.author, license:.license, bugs:.bugs, homepage:.homepage}' < package.json > dist/package.json && yarn testwp",
63-
"publish": "yarn version patch && yarn run prepare && cd dist && yarn publish --no-git-tag-version --new-version `git describe --abbrev=0 --tags` && cd ..",
63+
"publish": "yarn version patch && yarn run prepare && cd dist && npm publish && cd ..",
6464
"precommit": "pretty-quick --staged",
6565
"test": "nyc mocha \"test/**/*.ts\" --require ts-node/register --require source-map-support/register --timeout=60000 --recursive",
6666
"clean": "npx ts-node test-scripts/cleanup.ts"

yarn.lock

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,6 @@ __metadata:
348348
webpack: ^5.98.0
349349
webpack-cli: ^6.0.1
350350
ws: ^8.18.0
351-
your-library-name: ^1.0.0
352351
languageName: unknown
353352
linkType: soft
354353

@@ -4195,13 +4194,6 @@ __metadata:
41954194
languageName: node
41964195
linkType: hard
41974196

4198-
"validator@npm:^13.6.0":
4199-
version: 13.12.0
4200-
resolution: "validator@npm:13.12.0"
4201-
checksum: fb8f070724770b1449ea1a968605823fdb112dbd10507b2802f8841cda3e7b5c376c40f18c84e6a7b59de320a06177e471554101a85f1fa8a70bac1a84e48adf
4202-
languageName: node
4203-
linkType: hard
4204-
42054197
"watchpack@npm:^2.4.1":
42064198
version: 2.4.2
42074199
resolution: "watchpack@npm:2.4.2"
@@ -4567,12 +4559,3 @@ __metadata:
45674559
checksum: f77b3d8d00310def622123df93d4ee654fc6a0096182af8bd60679ddcdfb3474c56c6c7190817c84a2785648cdee9d721c0154eb45698c62176c322fb46fc700
45684560
languageName: node
45694561
linkType: hard
4570-
4571-
"your-library-name@npm:^1.0.0":
4572-
version: 1.0.0
4573-
resolution: "your-library-name@npm:1.0.0"
4574-
dependencies:
4575-
validator: ^13.6.0
4576-
checksum: c832e194b616943b371d97bb664adf30d168136cd2e8bd32d749d205c70a955c1720d38bd5a3de98bc0cf7aaad70bcac4dc82a1946179c5d4e6447fedc49473a
4577-
languageName: node
4578-
linkType: hard

0 commit comments

Comments
 (0)