nayota-show-sdk 1.3.80 → 1.3.82

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nayota-show-sdk",
3
- "version": "1.3.80",
3
+ "version": "1.3.82",
4
4
  "description": "nayota-show-server rest-api",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -90,7 +90,20 @@ function mapAreaToIotQuery(query = {}) {
90
90
  return next
91
91
  }
92
92
 
93
- function mapAreaToIotBody(data = {}) {
93
+ function resolveOptionalLegacyReference(data, primaryKey, legacyKey) {
94
+ if (hasOwn(data, primaryKey)) {
95
+ return data[primaryKey]
96
+ }
97
+
98
+ if (hasOwn(data, legacyKey)) {
99
+ return resolveLegacyReference(data[legacyKey])
100
+ }
101
+
102
+ return undefined
103
+ }
104
+
105
+ function mapAreaToIotBody(data = {}, options = {}) {
106
+ const isPartial = options.partial === true
94
107
  const metadata = mergeMetadata(data.metadata, {
95
108
  legacyType: data.type
96
109
  })
@@ -108,13 +121,13 @@ function mapAreaToIotBody(data = {}) {
108
121
  'relationInfo',
109
122
  'centerCoordinate'
110
123
  ]),
111
- code: data.code || fallbackCode(data.name || data._id, 'SPACE'),
112
- spaceType: data.spaceType || 'zone',
124
+ code: data.code || (isPartial ? undefined : fallbackCode(data.name || data._id, 'SPACE')),
125
+ spaceType: data.spaceType || (isPartial ? undefined : 'zone'),
113
126
  hierarchyId: data.hierarchyId || data.depart,
114
127
  isVisible: data.isVisible != null ? data.isVisible : data.visible,
115
128
  area: data.area != null ? data.area : data.areaSize,
116
- areaClassId: data.areaClassId || resolveLegacyReference(data.areaClass),
117
- targetHierarchyId: data.targetHierarchyId || resolveLegacyReference(data.targetDepartId),
129
+ areaClassId: resolveOptionalLegacyReference(data, 'areaClassId', 'areaClass'),
130
+ targetHierarchyId: resolveOptionalLegacyReference(data, 'targetHierarchyId', 'targetDepartId'),
118
131
  centerPoint: normalizeAreaCenterPoint(data),
119
132
  floorPlan: normalizeAreaFloorPlan(data),
120
133
  statisticsJson: parseLegacyJson(data.statisticsJson),
@@ -739,6 +752,35 @@ function mapIotDeviceClassToLegacy(item = {}) {
739
752
  })
740
753
  }
741
754
 
755
+ const LEGACY_CAMERA_DEVICE_CLASS = Object.freeze({
756
+ _id: 'camera',
757
+ id: 'camera',
758
+ name: '摄像头',
759
+ icon: '摄像头监控',
760
+ type: 'camera'
761
+ })
762
+
763
+ function appendLegacyCameraDeviceClass(response = {}) {
764
+ const rows = Array.isArray(response?.data?.rows) ? response.data.rows : []
765
+ const hasCameraType = rows.some(item => getLegacyId(item) === LEGACY_CAMERA_DEVICE_CLASS._id)
766
+
767
+ if (hasCameraType) {
768
+ return response
769
+ }
770
+
771
+ return {
772
+ ...response,
773
+ data: {
774
+ ...response.data,
775
+ total: Number(response?.data?.total ?? rows.length) + 1,
776
+ rows: [
777
+ ...rows,
778
+ { ...LEGACY_CAMERA_DEVICE_CLASS }
779
+ ]
780
+ }
781
+ }
782
+ }
783
+
742
784
  function createBasicCrudOperations({
743
785
  resource,
744
786
  mapListQuery = query => query,
@@ -764,7 +806,7 @@ function createBasicCrudOperations({
764
806
  return {
765
807
  url: resource,
766
808
  method: 'post',
767
- data: mapWritePayload(data)
809
+ data: mapWritePayload(data, { partial: false, action: 'create' })
768
810
  }
769
811
  },
770
812
  fromResponse(response) {
@@ -787,7 +829,7 @@ function createBasicCrudOperations({
787
829
  return {
788
830
  url: `${resource}/${getLegacyId(data)}`,
789
831
  method: 'patch',
790
- data: omitKeys(mapWritePayload(data), ['id', '_id'])
832
+ data: omitKeys(mapWritePayload(data, { partial: true, action: 'updateOne' }), ['id', '_id'])
791
833
  }
792
834
  },
793
835
  fromResponse(response) {
@@ -830,6 +872,26 @@ function createBasicCrudOperations({
830
872
  return operations
831
873
  }
832
874
 
875
+ function createDeviceClassOperations() {
876
+ const operations = createBasicCrudOperations({
877
+ resource: '/digital-twin-types',
878
+ mapListQuery: mapDeviceClassToIotQuery,
879
+ mapWritePayload: mapDeviceClassToIotBody,
880
+ mapItem: mapIotDeviceClassToLegacy,
881
+ enableDeleteMany: true
882
+ })
883
+ const fromListResponse = operations.list.fromResponse
884
+
885
+ operations.list = {
886
+ ...operations.list,
887
+ fromResponse(response) {
888
+ return appendLegacyCameraDeviceClass(fromListResponse(response))
889
+ }
890
+ }
891
+
892
+ return operations
893
+ }
894
+
833
895
  export const iotModuleSpecs = {
834
896
  area: {
835
897
  operations: createBasicCrudOperations({
@@ -925,13 +987,7 @@ export const iotModuleSpecs = {
925
987
  }
926
988
  },
927
989
  deviceClass: {
928
- operations: createBasicCrudOperations({
929
- resource: '/digital-twin-types',
930
- mapListQuery: mapDeviceClassToIotQuery,
931
- mapWritePayload: mapDeviceClassToIotBody,
932
- mapItem: mapIotDeviceClassToLegacy,
933
- enableDeleteMany: true
934
- })
990
+ operations: createDeviceClassOperations()
935
991
  }
936
992
  }
937
993
 
@@ -119,6 +119,52 @@ describe('iotModuleSpecs deviceClass UI compatibility', () => {
119
119
 
120
120
  expect(response.data.rows[0].icon).toBe('tabler-current-panel')
121
121
  })
122
+
123
+ test('appends legacy virtual camera device class to v2 list responses', () => {
124
+ const response = deviceClass.list.fromResponse({
125
+ code: 0,
126
+ data: {
127
+ total: 1,
128
+ items: [
129
+ {
130
+ id: 'type-1',
131
+ typeCode: 'LIGHT',
132
+ name: '照明'
133
+ }
134
+ ]
135
+ }
136
+ })
137
+
138
+ expect(response.data.total).toBe(2)
139
+ expect(response.data.rows).toContainEqual({
140
+ _id: 'camera',
141
+ id: 'camera',
142
+ name: '摄像头',
143
+ icon: '摄像头监控',
144
+ type: 'camera'
145
+ })
146
+ })
147
+
148
+ test('does not duplicate legacy virtual camera device class when backend already returns it', () => {
149
+ const response = deviceClass.list.fromResponse({
150
+ code: 0,
151
+ data: {
152
+ total: 1,
153
+ items: [
154
+ {
155
+ id: 'camera',
156
+ name: '摄像头',
157
+ type: 'camera'
158
+ }
159
+ ]
160
+ }
161
+ })
162
+
163
+ const cameraRows = response.data.rows.filter(item => item._id === 'camera')
164
+
165
+ expect(response.data.total).toBe(1)
166
+ expect(cameraRows).toHaveLength(1)
167
+ })
122
168
  })
123
169
 
124
170
  describe('iotModuleSpecs departs hierarchy compatibility', () => {
@@ -189,6 +235,32 @@ describe('iotModuleSpecs area space compatibility', () => {
189
235
 
190
236
  expect(response.data.rows[0].centerCoordinate).toEqual([12, 34])
191
237
  })
238
+
239
+ test('does not clear areaClassId on partial statistics sync updates', () => {
240
+ const request = area.updateOne.toRequest({
241
+ _id: '4f168184-9ef0-4068-b494-2ee861db0c08',
242
+ statisticsJson: '[{"componentName":"WeeklyEnergyConsumptionTrend"}]'
243
+ })
244
+
245
+ expect(request.url).toBe('/spaces/4f168184-9ef0-4068-b494-2ee861db0c08')
246
+ expect(request.method).toBe('patch')
247
+ expect(request.data.statisticsJson).toEqual([
248
+ { componentName: 'WeeklyEnergyConsumptionTrend' }
249
+ ])
250
+ expect(request.data.areaClassId).toBeUndefined()
251
+ expect(request.data.targetHierarchyId).toBeUndefined()
252
+ expect(request.data.code).toBeUndefined()
253
+ expect(request.data.spaceType).toBeUndefined()
254
+ })
255
+
256
+ test('keeps explicit areaClass clear on area updates', () => {
257
+ const request = area.updateOne.toRequest({
258
+ _id: '4f168184-9ef0-4068-b494-2ee861db0c08',
259
+ areaClass: ''
260
+ })
261
+
262
+ expect(request.data.areaClassId).toBeNull()
263
+ })
192
264
  })
193
265
 
194
266
  describe('iotModuleSpecs devices write compatibility', () => {