Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,6 @@ jobs:

- name: Vitest
run: npm test

- name: Smoke tests
run: npm run test:smoke
1 change: 1 addition & 0 deletions vueManager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"preview": "vite preview",
"lint": "eslint . --fix",
"lint:ci": "eslint . --max-warnings 0",
"test:smoke": "node --test tests/*.test.js",
"format": "prettier --write \"src/**/*.{js,vue,scss,css}\"",
"format:check": "prettier --check \"src/**/*.{js,vue,scss,css}\"",
"lint:all": "npm run lint && npm run format:check && npm run lint:scss",
Expand Down
49 changes: 29 additions & 20 deletions vueManager/src/request.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
/**
* Extract payload from MODX connector JSON envelope.
*
* Connector responses use `{ success, message, object?, data? }`. Callers expect
* the inner payload (e.g. `{ results, total }`), not the envelope with `success`.
*
* @param {unknown} responseData - Parsed JSON body from connector.php
* @returns {unknown} Unwrapped payload, or the original value when not an envelope
*
* Uses `!= null` so falsy scalars in `object`/`data` (0, false, "") are valid payloads.
*/
export function unwrapResponsePayload(responseData) {
if (responseData === null || typeof responseData !== 'object' || Array.isArray(responseData)) {
return responseData
}

if ('object' in responseData && responseData.object != null) {
return responseData.object
}

if ('data' in responseData && responseData.data != null) {
return responseData.data
}

return responseData
}

/**
* API Request class for working with MiniShop3 API through MODX connector
*
Expand Down Expand Up @@ -126,19 +153,7 @@ class Request {
)
}

if (responseData.object && Object.keys(responseData.object).length > 0) {
return responseData.object
} else if (
responseData.data &&
Array.isArray(responseData.data) &&
responseData.data.length > 0
) {
return responseData.data
} else if (responseData.data && !Array.isArray(responseData.data)) {
return responseData.data
}

return responseData
return unwrapResponsePayload(responseData)
} catch (error) {
if (error instanceof RequestError) {
throw error
Expand Down Expand Up @@ -236,13 +251,7 @@ class Request {
)
}

if (responseData.object && Object.keys(responseData.object).length > 0) {
return responseData.object
} else if (responseData.data) {
return responseData.data
}

return responseData
return unwrapResponsePayload(responseData)
} catch (error) {
if (error instanceof RequestError) {
throw error
Expand Down
55 changes: 55 additions & 0 deletions vueManager/tests/requestUnwrap.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import assert from 'node:assert/strict'
import test from 'node:test'

import { unwrapResponsePayload } from '../src/request.js'

test('returns empty object payload instead of envelope', () => {
const envelope = { success: true, message: '', object: {} }
assert.deepEqual(unwrapResponsePayload(envelope), {})
})

test('returns list payload with empty results', () => {
const envelope = { success: true, message: '', object: { results: [], total: 0 } }
assert.deepEqual(unwrapResponsePayload(envelope), { results: [], total: 0 })
})

test('returns empty data array instead of envelope', () => {
const envelope = { success: true, message: '', data: [] }
assert.deepEqual(unwrapResponsePayload(envelope), [])
})

test('returns object array payload', () => {
const envelope = { success: true, message: '', object: [] }
assert.deepEqual(unwrapResponsePayload(envelope), [])
})

test('prefers object field over data field', () => {
const envelope = { success: true, object: { a: 1 }, data: { b: 2 } }
assert.deepEqual(unwrapResponsePayload(envelope), { a: 1 })
})

test('returns non-array data object', () => {
const envelope = { success: true, data: { import_id: 'abc' } }
assert.deepEqual(unwrapResponsePayload(envelope), { import_id: 'abc' })
})

test('falls back to envelope when payload fields are null', () => {
const envelope = { success: true, message: 'Deleted', object: null, data: null }
assert.equal(unwrapResponsePayload(envelope), envelope)
})

test('returns envelope when no payload keys', () => {
const envelope = { success: true, message: 'ok' }
assert.equal(unwrapResponsePayload(envelope), envelope)
})

test('returns falsy scalar object payload', () => {
assert.equal(unwrapResponsePayload({ success: true, object: 0 }), 0)
assert.equal(unwrapResponsePayload({ success: true, object: false }), false)
assert.equal(unwrapResponsePayload({ success: true, object: '' }), '')
})

test('passes through non-object values', () => {
assert.equal(unwrapResponsePayload(null), null)
assert.equal(unwrapResponsePayload('ok'), 'ok')
})