nayota-show-sdk 1.3.92 → 1.3.94

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,138 @@
1
1
  import requestAlarm from '../utils/alarm-request'
2
+ import { isV2 } from '../utils/version'
3
+ import { getOne as getDeviceClassOne } from './deviceClass'
4
+
5
+ function getLegacyRefId(value) {
6
+ if (value == null) return ''
7
+
8
+ if (typeof value === 'string' || typeof value === 'number') {
9
+ return String(value)
10
+ }
11
+
12
+ if (typeof value === 'object') {
13
+ if (value._id != null) return String(value._id)
14
+ if (value.id != null) return String(value.id)
15
+ }
16
+
17
+ return ''
18
+ }
19
+
20
+ function getLegacyRefName(value) {
21
+ if (!value || typeof value !== 'object') return ''
22
+ return value.name || value.deviceClassName || value.typeName || value.label || ''
23
+ }
24
+
25
+ function normalizeLegacyDeviceClass(value, fallbackId = '') {
26
+ if (!value || typeof value !== 'object') return value
27
+
28
+ const id = getLegacyRefId(value) || fallbackId
29
+ return {
30
+ ...value,
31
+ _id: value._id || id || undefined,
32
+ id: value.id || id || undefined,
33
+ name: getLegacyRefName(value) || undefined
34
+ }
35
+ }
36
+
37
+ function getAlarmRows(response) {
38
+ if (Array.isArray(response?.data?.rows)) return response.data.rows
39
+ if (Array.isArray(response?.rows)) return response.rows
40
+ return []
41
+ }
42
+
43
+ function withAlarmRows(response, rows) {
44
+ if (Array.isArray(response?.data?.rows)) {
45
+ return {
46
+ ...response,
47
+ data: {
48
+ ...response.data,
49
+ rows
50
+ }
51
+ }
52
+ }
53
+
54
+ if (Array.isArray(response?.rows)) {
55
+ return {
56
+ ...response,
57
+ rows
58
+ }
59
+ }
60
+
61
+ return response
62
+ }
63
+
64
+ async function loadDeviceClassMap(ids = []) {
65
+ const uniqueIds = [...new Set(ids.filter(Boolean))]
66
+ const entries = await Promise.all(uniqueIds.map(async id => {
67
+ try {
68
+ const response = await getDeviceClassOne(id)
69
+ const item = response?.data
70
+ const normalized = normalizeLegacyDeviceClass(item, id)
71
+ return [id, normalized && getLegacyRefName(normalized) ? normalized : null]
72
+ } catch (error) {
73
+ return [id, null]
74
+ }
75
+ }))
76
+
77
+ return new Map(entries.filter(([, item]) => item))
78
+ }
79
+
80
+ async function normalizeV2AlarmRecordListResponse(response) {
81
+ if (!isV2()) return response
82
+
83
+ const rows = getAlarmRows(response)
84
+ if (!rows.length) return response
85
+
86
+ const rowsWithLocalDeviceClass = rows.map(row => {
87
+ const deviceClass = row?.deviceClass
88
+ const deviceDeviceClass = row?.device?.deviceClass
89
+
90
+ if (getLegacyRefName(deviceClass)) {
91
+ return {
92
+ ...row,
93
+ deviceClass: normalizeLegacyDeviceClass(deviceClass)
94
+ }
95
+ }
96
+
97
+ if (getLegacyRefName(deviceDeviceClass)) {
98
+ return {
99
+ ...row,
100
+ deviceClass: normalizeLegacyDeviceClass(
101
+ deviceDeviceClass,
102
+ getLegacyRefId(deviceClass)
103
+ )
104
+ }
105
+ }
106
+
107
+ return row
108
+ })
109
+
110
+ const missingIds = rowsWithLocalDeviceClass
111
+ .filter(row => row?.deviceClass && !getLegacyRefName(row.deviceClass))
112
+ .map(row => getLegacyRefId(row.deviceClass))
113
+
114
+ if (!missingIds.length) {
115
+ return withAlarmRows(response, rowsWithLocalDeviceClass)
116
+ }
117
+
118
+ const deviceClassMap = await loadDeviceClassMap(missingIds)
119
+ if (!deviceClassMap.size) {
120
+ return withAlarmRows(response, rowsWithLocalDeviceClass)
121
+ }
122
+
123
+ return withAlarmRows(response, rowsWithLocalDeviceClass.map(row => {
124
+ if (!row?.deviceClass || getLegacyRefName(row.deviceClass)) return row
125
+
126
+ const id = getLegacyRefId(row.deviceClass)
127
+ const deviceClass = deviceClassMap.get(id)
128
+ if (!deviceClass) return row
129
+
130
+ return {
131
+ ...row,
132
+ deviceClass
133
+ }
134
+ }))
135
+ }
2
136
  /**
3
137
  * @file 报警记录api
4
138
  * @module 报警记录接口
@@ -324,7 +458,7 @@ export function list(query) {
324
458
  url: '/alarm-records',
325
459
  method: 'get',
326
460
  params: query
327
- })
461
+ }).then(normalizeV2AlarmRecordListResponse)
328
462
  }
329
463
 
330
464
  /**
@@ -0,0 +1,126 @@
1
+ jest.mock('../utils', () => ({
2
+ requestShow: jest.fn(),
3
+ requestIot: jest.fn(),
4
+ requestForm: jest.fn()
5
+ }))
6
+
7
+ const urlcfg = require('../config/urlcfg').default
8
+ const { requestIot, requestShow } = require('../utils')
9
+ const { list } = require('./alarmRecord')
10
+
11
+ describe('alarmRecord api compatibility', () => {
12
+ const originalVersion = urlcfg.version
13
+ const originalIotServer = urlcfg.iotServer
14
+
15
+ afterEach(() => {
16
+ urlcfg.version = originalVersion
17
+ urlcfg.iotServer = originalIotServer
18
+ jest.clearAllMocks()
19
+ })
20
+
21
+ test('keeps v1 list response unchanged', async () => {
22
+ const response = {
23
+ code: 0,
24
+ data: {
25
+ rows: [
26
+ {
27
+ _id: 'alarm-1',
28
+ deviceClass: 'type-1'
29
+ }
30
+ ]
31
+ }
32
+ }
33
+ urlcfg.version = 'v1'
34
+ requestShow.mockResolvedValue(response)
35
+
36
+ await expect(list({ limit: 1 })).resolves.toBe(response)
37
+ expect(requestShow).toHaveBeenCalledWith({
38
+ url: '/alarm-records',
39
+ method: 'get',
40
+ params: { limit: 1 }
41
+ })
42
+ expect(requestIot).not.toHaveBeenCalled()
43
+ })
44
+
45
+ test('fills v2 alarm deviceClass name from digital twin type detail', async () => {
46
+ urlcfg.version = 'v2'
47
+ urlcfg.iotServer = '/iot-api'
48
+ requestIot.mockImplementation(config => {
49
+ if (config.url === '/alarm-records') {
50
+ return Promise.resolve({
51
+ code: 0,
52
+ data: {
53
+ total: 1,
54
+ rows: [
55
+ {
56
+ _id: 'alarm-1',
57
+ device: { name: '安消智能摄像头' },
58
+ deviceClass: 'type-1',
59
+ subType: '事件告警'
60
+ }
61
+ ]
62
+ }
63
+ })
64
+ }
65
+
66
+ if (config.url === '/digital-twin-types/type-1') {
67
+ return Promise.resolve({
68
+ code: 0,
69
+ data: {
70
+ id: 'type-1',
71
+ name: '安消智能摄像头',
72
+ typeCode: 'CAMERA'
73
+ }
74
+ })
75
+ }
76
+
77
+ return Promise.reject(new Error(`unexpected request: ${config.url}`))
78
+ })
79
+
80
+ const response = await list({ limit: 1 })
81
+
82
+ expect(response.data.rows[0].deviceClass).toMatchObject({
83
+ _id: 'type-1',
84
+ id: 'type-1',
85
+ name: '安消智能摄像头',
86
+ code: 'CAMERA'
87
+ })
88
+ expect(requestIot).toHaveBeenCalledWith({
89
+ url: '/alarm-records',
90
+ method: 'get',
91
+ params: { limit: 1 }
92
+ })
93
+ expect(requestIot).toHaveBeenCalledWith({
94
+ url: '/digital-twin-types/type-1',
95
+ method: 'get'
96
+ })
97
+ })
98
+
99
+ test('does not request device class detail when v2 row already has name', async () => {
100
+ urlcfg.version = 'v2'
101
+ urlcfg.iotServer = '/iot-api'
102
+ requestIot.mockResolvedValue({
103
+ code: 0,
104
+ data: {
105
+ rows: [
106
+ {
107
+ _id: 'alarm-1',
108
+ deviceClass: {
109
+ _id: 'type-1',
110
+ name: '摄像头'
111
+ }
112
+ }
113
+ ]
114
+ }
115
+ })
116
+
117
+ const response = await list({ limit: 1 })
118
+
119
+ expect(response.data.rows[0].deviceClass).toMatchObject({
120
+ _id: 'type-1',
121
+ id: 'type-1',
122
+ name: '摄像头'
123
+ })
124
+ expect(requestIot).toHaveBeenCalledTimes(1)
125
+ })
126
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nayota-show-sdk",
3
- "version": "1.3.92",
3
+ "version": "1.3.94",
4
4
  "description": "nayota-show-server rest-api",
5
5
  "type": "module",
6
6
  "main": "index.js",