Repository navigation
Expand file tree
/
Copy pathflashApi.js
More file actions
143 lines (125 loc) · 5.69 KB
/
Copy pathflashApi.js
File metadata and controls
143 lines (125 loc) · 5.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import store from '@/store';
import {
decodeEmbeddedFlashId,
decodeEmbeddedPartNumber,
getEmbeddedInfo,
searchEmbeddedFlashId,
searchEmbeddedPartNumber,
warmEmbeddedParser as warmEmbeddedFdnextParser
} from '@/services/fdnextApi';
import { FDNEXT_CAPABILITIES_SCHEMA_VERSION, FDNEXT_RESULT_SCHEMA_VERSION, summaryText } from '@/services/fdnextResultView';
import { DEFAULT_HTTP_REQUEST_TIMEOUT_MS, runWithRequestTimeout } from '@/services/requestControl';
import { automaticRequest } from '@/services/automaticRequests';
const makeUrl = (endpoint, params = {}) => {
const base = store.getServerAddress().replace(/\/+$/, '');
const url = new URL(`${base}/${endpoint.replace(/^\/+/, '')}`);
for (const [key, value] of Object.entries(params)) {
if (value !== undefined && value !== null && value !== '') {
url.searchParams.set(key, value);
}
}
return url.toString();
};
const assertFdnextPayload = (payload, schemaVersion, endpoint) => {
if (!payload || payload.schemaVersion !== schemaVersion) {
throw new Error(`Unsupported fdnext response from ${endpoint}`);
}
if (payload.status === 'ok' && ['part.decode', 'identifier.decode'].includes(payload.operation)
&& (!Array.isArray(payload.summary?.brief) || !Array.isArray(payload.summary?.full))) {
throw new Error(`Missing fdnext summary from ${endpoint}`);
}
return payload;
};
const parseResponsePayload = async response => {
const text = await response.text();
if (!text) return null;
try {
return JSON.parse(text);
} catch {
return null;
}
};
const request = async (endpoint, params = {}, schemaVersion = FDNEXT_RESULT_SCHEMA_VERSION, options = {}) => {
return runWithRequestTimeout(async signal => {
const response = await fetch(makeUrl(endpoint, params), { signal });
const payload = await parseResponsePayload(response);
if (payload) {
try {
return assertFdnextPayload(payload, schemaVersion, endpoint);
} catch (err) {
if (response.ok) throw err;
}
}
if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);
throw new Error(`Unsupported fdnext response from ${endpoint}`);
}, {
signal: options.signal,
timeoutMs: options.timeoutMs ?? DEFAULT_HTTP_REQUEST_TIMEOUT_MS,
timeoutMessage: `Request to ${endpoint} timed out.`
});
};
const useEmbeddedParser = () => store.isEmbeddedParser();
// Also used to decide whether a displayed result still matches a copy request.
export const lookupContextKey = (operation, query, limit = 0, timeoutMs = null) => JSON.stringify([
operation, query, store.getLang(), useEmbeddedParser() ? 'embedded' : store.getServerAddress().replace(/\/+$/, ''),
store.getControllerGroupParam(), limit, timeoutMs
]);
const searchRequest = (operation, query, limit, options, task) => {
const execute = requestOptions => useEmbeddedParser() && requestOptions.timeoutMs
? runWithRequestTimeout(signal => task({ ...requestOptions, signal }), requestOptions)
: task(requestOptions);
return options.automatic
? automaticRequest(lookupContextKey(operation, query, limit, options.timeoutMs), signal => execute({ ...options, signal }), options.signal)
: execute(options);
};
const langParams = () => ({
lang: store.getLang()
});
const limitParams = limit => {
const value = Number(limit);
return Number.isFinite(value) && value > 0 ? { limit: value } : {};
};
const controllerGroupParams = () => ({
controllerGroup: store.getControllerGroupParam()
});
export const getServerInfo = async (options = {}) => useEmbeddedParser()
? getEmbeddedInfo(options)
: request('capabilities', langParams(), FDNEXT_CAPABILITIES_SCHEMA_VERSION, options);
export const warmEmbeddedParser = () => {
if (!useEmbeddedParser()) return Promise.resolve();
return warmEmbeddedFdnextParser();
};
export const decodePartNumber = async (pn, options = {}) => {
return useEmbeddedParser() ? decodeEmbeddedPartNumber(pn, options) : request('parts/decode', {
...langParams(),
...controllerGroupParams(),
query: pn
}, FDNEXT_RESULT_SCHEMA_VERSION, options);
};
export const searchPartNumber = async (pn, limit = 0, options = {}) => {
return searchRequest('parts/search', pn, limit, options, requestOptions => useEmbeddedParser() ? searchEmbeddedPartNumber(pn, limit, requestOptions) : request('parts/search', {
...langParams(),
query: pn,
...limitParams(limit)
}, FDNEXT_RESULT_SCHEMA_VERSION, requestOptions));
};
export const summarizePartNumber = async (pn, { mode = 'brief', ...options } = {}) => summaryText(await decodePartNumber(pn, options), mode);
export const decodeFlashId = async (id, options = {}) => {
const input = { idScheme: 'nand.flash_id' };
return useEmbeddedParser() ? decodeEmbeddedFlashId(id, options) : request('identifiers/decode', {
...langParams(),
query: id,
...input,
...controllerGroupParams()
}, FDNEXT_RESULT_SCHEMA_VERSION, options);
};
export const searchFlashId = async (id, limit = 0, options = {}) => {
const input = { idScheme: 'nand.flash_id' };
return searchRequest('identifiers/search', id, limit, options, requestOptions => useEmbeddedParser() ? searchEmbeddedFlashId(id, limit, requestOptions) : request('identifiers/search', {
...langParams(),
query: id,
...input,
...limitParams(limit)
}, FDNEXT_RESULT_SCHEMA_VERSION, requestOptions));
};
export const summarizeFlashId = async (id, { mode = 'brief', ...options } = {}) => summaryText(await decodeFlashId(id, options), mode);