Skip to content

Commit

Permalink
Updated mapping error handling and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-yao committed Nov 19, 2019
1 parent d3d3ecf commit 86ef425
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 31 deletions.
81 changes: 57 additions & 24 deletions packages/ripple-nuxt-tide/lib/core/mapping.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,36 +27,51 @@ export class Mapping {
}

get (data, type = 'tideField') {
let result
let dataMode = 'single'
let result = []
let dataMode

if (Array.isArray(data)) {
dataMode = 'array'
// If the data is array, return an array.
result = []
for (const item of data) {
// Mapping items only for those in mapping configs.
if (this.mappingConfig[type][item.type]) {
const component = this[_getComponent](item, type)
if (component) {
result.push(component)
}
} else {
dataMode = 'single'
data = [data]
}

for (const item of data) {
// Mapping items only for those in mapping configs.
if (this.mappingConfig[type][item.type]) {
const component = this[_getComponent](item, type)
result.push(component)
} else {
if (process.server) {
logger.warn(`"${item.type}" is not a supported component in map.`, { label: 'Mapping' })
}
}
} else {
// If the data is just one item object, return a single item.
result = this[_getComponent](data, type) || {}
}

// Return a promise as some props' value need to be fetched from Tide.
return new Promise(function (resolve, reject) {
if (dataMode === 'single') {
resolve(result)
if (result[0]) {
result[0].catch(error => {
if (process.server) {
logger.error('Mapping failed to get result due to a error.', { error, label: 'Mapping' })
}
reject(error)
})
resolve(result[0])
} else {
reject(new Error('Mapping failed to get result.'))
}
} else {
let allFetched = Promise.all(result).catch(error => {
reject(new Error(`Mapping failed by resolve the fetching. Error: ${error}`))
Promise.all(result).catch(error => {
if (process.server) {
logger.error('Mapping failed to get result due to a error.', { error, label: 'Mapping' })
}
reject(error)
}).then(res => {
resolve(res)
})
resolve(allFetched)
}
})
}
Expand All @@ -70,6 +85,9 @@ export class Mapping {
for (const filter of filters) {
const mapping = this
// Pass mapping instance into filters, so we can use filters inside filter.
if (typeof this.mappingFilters[filter] !== 'function') {
return new Error(`Mapping filter "${filter}" is not a function or not defined.`)
}
fieldValue = this.mappingFilters[filter](fieldValue, { mapping })
}
return fieldValue
Expand Down Expand Up @@ -129,10 +147,6 @@ export class Mapping {
default:
let itemConfig = this.mappingConfig[type][item.type]

if (typeof itemConfig === 'undefined') {
throw new Error(`"${item.type}" is not a supported component in map.`)
}

// If it's one to multiple mode, we switch to the right one based on expression.
if (this.mappingConfig[type][item.type].components) {
const components = this.mappingConfig[type][item.type].components
Expand All @@ -143,9 +157,14 @@ export class Mapping {
itemConfig = component[0]
}

const data = await this[_getProps](item, itemConfig)
if (data instanceof Error) {
throw data
}

return {
name: this[_getName](itemConfig),
data: await this[_getProps](item, itemConfig),
data: data,
cols: this[_getCols](itemConfig),
childCols: this[_getChildCols](itemConfig),
class: this[_getClass](itemConfig),
Expand Down Expand Up @@ -233,12 +252,26 @@ export class Mapping {
const propMapping = itemConfig.props[prop]
const getProp = this[_getFieldVal](propMapping, item).then(res => {
props[prop] = res
}).catch(error => {
logger.error('Failed to get prop "%s" value.', prop, { error, label: 'Mapping' })
})
getProps.push(getProp)
}
return new Promise(function (resolve, reject) {
const allFetched = Promise.all(getProps).then(() => {
return props
// If we got any coding error(e.g. custom mapping configuration error), stop doing further and fail the mapping.
// So developer can notice it and fix it.
let getPropsError = null
const propsValues = Object.values(props)
const noError = propsValues.every(value => {
if (value instanceof Error) {
getPropsError = value
return false
}
return true
})

return noError ? props : getPropsError
}).catch(error => {
reject(new Error(`Get props failed by resolve the fetching. Error: ${error}`))
})
Expand Down
15 changes: 11 additions & 4 deletions packages/ripple-nuxt-tide/lib/core/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ export default async function (context, pageData) {
if (typeof context.res !== 'undefined') {
context.res.statusCode = 500
}
logger.error('Failed to get the page data.', { error })
if (process.server) {
logger.error('Failed to get the page data.', { error, label: 'Middleware' })
}
}
}
}
Expand All @@ -115,6 +117,10 @@ export default async function (context, pageData) {
const addComponentFromPromise = (promise, name) => {
const addMapping = promise.then(res => {
pageData.tidePage[name] = res
}).catch(error => {
if (process.server) {
logger.error('Failed to add component for "%s"', name, { error, label: 'Middleware' })
}
})
asyncTasks.push(addMapping)
}
Expand Down Expand Up @@ -203,7 +209,9 @@ export default async function (context, pageData) {
const siteSectionData = await context.app.$tide.getSiteData(headersConfig, pageData.tidePage.section)

if (siteSectionData instanceof Error) {
logger.error('Could not get site section data from Tide API.', { error: siteSectionData, label: 'Middleware' })
if (process.server) {
logger.error('Could not get site section data from Tide API.', { error: siteSectionData, label: 'Middleware' })
}
} else {
// Section navigation component will only use the main menu.
const addSectionNavMenu = siteSectionData.hierarchicalMenus.menuMain
Expand Down Expand Up @@ -253,9 +261,8 @@ export default async function (context, pageData) {
}
}
} catch (error) {
// TODO: Take some action if above mapping error happens.
if (process.server) {
logger.error('Failed to get the mapped component.', { error })
logger.error('Failed to get the mapped component.', { error, label: 'Middleware' })
}
}

Expand Down
47 changes: 44 additions & 3 deletions packages/ripple-nuxt-tide/test/unit/mapping.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ describe('mapping', () => {
class: ['test-class-a', 'test-class-b']
},

testUndefinedFilter: {
component: 'rpl-test-component',
props: {
a: {
field: 'a',
filters: ['undefinedFilter']
},
b: 'b'
}
},

testFetchItem: {
component: 'rpl-test-fetch-component',
props: {
Expand Down Expand Up @@ -193,16 +204,46 @@ describe('mapping', () => {

test('should get error by given a item not in mapping config', async () => {
const mapping = new Mapping(config)
expect.assertions(1)
expect.assertions(2)

const item = {
type: 'testItemNotMapped'
}

try {
// Test single mode
await mapping.get(item, 'testField')
} catch (e) {
expect(e).toEqual(new Error('"testItemNotMapped" is not a supported component in map.'))
expect(e).toEqual(new Error('Mapping failed to get result.'))
}

// Test array mode
const result = await mapping.get([item], 'testField')
expect(result).toEqual([])
})

test('should get error by given an undefined filter in mapping config', async () => {
const mapping = new Mapping(config)
expect.assertions(2)

const item = {
type: 'testUndefinedFilter',
a: 'value a',
b: 'value b'
}

try {
// Test single mode
await mapping.get(item, 'testField')
} catch (e) {
expect(e).toEqual(new Error('Mapping filter "undefinedFilter" is not a function or not defined.'))
}

try {
// Test array mode
await mapping.get([item], 'testField')
} catch (e) {
expect(e).toEqual(new Error('Mapping filter "undefinedFilter" is not a function or not defined.'))
}
})

Expand Down Expand Up @@ -244,7 +285,7 @@ describe('mapping', () => {
expect(result).toEqual(components)
})

test('should still get resovled if fetcher failed', async () => {
test('should still get resolved if fetcher failed', async () => {
const mapping = new Mapping(config, tideApi)
expect.assertions(1)

Expand Down

0 comments on commit 86ef425

Please sign in to comment.