Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[OP#41289] list all linked workpackages #71

57 changes: 12 additions & 45 deletions src/components/tab/SearchInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { translate as t } from '@nextcloud/l10n'
import Multiselect from '@nextcloud/vue/dist/Components/Multiselect'
import WorkPackage from './WorkPackage'
import { showError } from '@nextcloud/dialogs'
import { workpackageHelper } from '../../utils/workpackageHelper'

const STATE_OK = 'ok'
const STATE_ERROR = 'error'
Expand Down Expand Up @@ -96,12 +97,6 @@ export default {
this.state = STATE_ERROR
}
},
replaceHrefToGetId(href) {
// this is a helper method replaces the string like this "/api/v3/types/3" to get id
return href
? href.replace(/.*\//, '')
: null
},
async linkWorkPackageToFile(selectedOption) {
const params = new URLSearchParams()
params.append('workpackageId', selectedOption.id)
Expand Down Expand Up @@ -148,48 +143,20 @@ export default {
if (this.isStateLoading) this.state = STATE_OK
},
async processWorkPackages(workPackages) {
for (const workPackage of workPackages) {
const statusId = this.replaceHrefToGetId(workPackage._links.status.href)
const typeId = this.replaceHrefToGetId(workPackage._links.type.href)
const userId = this.replaceHrefToGetId(workPackage._links.assignee.href)
const userName = workPackage._links.assignee.title
const avatarUrl = generateUrl('/apps/integration_openproject/avatar?')
+ encodeURIComponent('userId')
+ '=' + userId
+ '&' + encodeURIComponent('userName')
+ '=' + userName
const statusColor = await this.getWorkPackageColorAttributes('/apps/integration_openproject/statuses/', statusId)
const typeColor = await this.getWorkPackageColorAttributes('/apps/integration_openproject/types/', typeId)
const selectedIdFound = this.selectedId.some(el => el.id === workPackage.id)
const workpackageIdFound = this.searchResults.some(el => el.id === workPackage.id)
if (!workpackageIdFound && !selectedIdFound) {
this.searchResults.push({
id: workPackage.id,
subject: workPackage.subject,
project: workPackage._links.project.title,
statusTitle: workPackage._links.status.title,
typeTitle: workPackage._links.type.title,
assignee: userName,
statusCol: statusColor,
typeCol: typeColor,
picture: avatarUrl,
})
for (let workPackage of workPackages) {
try {
workPackage = await workpackageHelper.getAdditionalMetaData(workPackage)
const selectedIdFound = this.selectedId.some(el => el.id === workPackage.id)
const workpackageIdFound = this.searchResults.some(el => el.id === workPackage.id)
if (!workpackageIdFound && !selectedIdFound) {
this.searchResults.push(workPackage)
}
} catch (e) {
console.error('could not process workpackage data')
}

}
},
async getWorkPackageColorAttributes(path, id) {
const url = generateUrl(path + id)
let response
try {
response = await axios.get(url)
} catch (e) {
response = e.response
}
this.checkForErrorCode(response.status)
return (response.status === 200 && response.data?.color)
? response.data.color
: ''
},
},
}
</script>
Expand Down
63 changes: 63 additions & 0 deletions src/utils/workpackageHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { generateUrl } from '@nextcloud/router'
import axios from '@nextcloud/axios'

export const workpackageHelper = {
async getColorAttributes(path, id) {
const url = generateUrl(path + id)
let response
try {
response = await axios.get(url)
} catch (e) {
response = e.response
}
return (response.status === 200 && response.data?.color)
? response.data.color
: ''
},
replaceHrefToGetId(href) {
// this is a helper method replaces the string like this "/api/v3/types/3" to get id
return href
? href.replace(/.*\//, '')
: null
},
async getAdditionalMetaData(workPackage) {
if (typeof workPackage._links.status.href !== 'string'
|| workPackage._links.status.href === ''
|| typeof workPackage._links.type.href !== 'string'
|| workPackage._links.type.href === ''
|| typeof workPackage.id !== 'number'
|| typeof workPackage.subject !== 'string'
|| workPackage.subject === ''
|| typeof workPackage._links.project.title !== 'string'
|| workPackage._links.project.title === ''
|| typeof workPackage._links.status.title !== 'string'
|| workPackage._links.status.title === ''
|| typeof workPackage._links.type.title !== 'string'
|| workPackage._links.type.title === ''
) {
throw new Error('missing data in workpackage object')
}
const statusId = this.replaceHrefToGetId(workPackage._links.status.href)
const typeId = this.replaceHrefToGetId(workPackage._links.type.href)
const userId = this.replaceHrefToGetId(workPackage._links.assignee.href)
const userName = workPackage._links.assignee.title
const avatarUrl = generateUrl('/apps/integration_openproject/avatar?')
+ encodeURIComponent('userId')
+ '=' + userId
+ '&' + encodeURIComponent('userName')
+ '=' + userName
const statusColor = await this.getColorAttributes('/apps/integration_openproject/statuses/', statusId)
const typeColor = await this.getColorAttributes('/apps/integration_openproject/types/', typeId)
return {
id: workPackage.id,
subject: workPackage.subject,
project: workPackage._links.project.title,
statusTitle: workPackage._links.status.title,
typeTitle: workPackage._links.type.title,
assignee: userName,
statusCol: statusColor,
typeCol: typeColor,
picture: avatarUrl,
}
},
}
19 changes: 16 additions & 3 deletions src/views/ProjectsTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import axios from '@nextcloud/axios'
import { generateUrl } from '@nextcloud/router'
import SearchInput from '../components/tab/SearchInput'
import { loadState } from '@nextcloud/initial-state'
import { workpackageHelper } from '../utils/workpackageHelper'

export default {
name: 'ProjectsTab',
Expand All @@ -60,7 +61,7 @@ export default {
},
data: () => ({
error: '',
fileInfo: null,
fileInfo: { },
state: 'loading',
workpackages: [],
requestUrl: loadState('integration_openproject', 'request-url'),
Expand All @@ -80,6 +81,7 @@ export default {
async update(fileInfo) {
this.fileInfo = fileInfo
this.workpackages = []
this.state = 'loading'
await this.fetchWorkpackages(this.fileInfo.id)
},
/**
Expand All @@ -100,14 +102,21 @@ export default {
if (!Array.isArray(response.data)) {
this.state = 'failed-fetching-workpackages'
} else {
// empty data means there are no workpackages linked
if (response.data.length > 0) {
for (let workPackage of response.data) {
workPackage = await workpackageHelper.getAdditionalMetaData(workPackage)
this.workpackages.push(workPackage)
}
}
this.state = 'ok'
}
} catch (error) {
if (error.response && error.response.status === 401) {
this.state = 'no-token'
} else if (error.response.status === 404) {
} else if (error.response && error.response.status === 404) {
this.state = 'connection-error'
} else if (error.response.status === 500) {
} else if (error.response && error.response.status === 500) {
this.state = 'error'
} else {
this.state = 'failed-fetching-workpackages'
Expand Down Expand Up @@ -146,5 +155,9 @@ export default {
padding-top: 0;
font-size: 1.2rem;
}

.icon-loading:after {
top: 140%;
}
}
</style>
20 changes: 19 additions & 1 deletion tests/jest/components/tab/SearchInput.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,24 @@ describe('SearchInput.vue tests', () => {
)
axiosSpy.mockRestore()
})
it('should log an error on invalid payload', async () => {
const axiosSpy = jest.spyOn(axios, 'get')
.mockImplementationOnce(() => Promise.resolve({
status: 200,
data: [{
id: 123,
}],
}))
const consoleMock = jest.spyOn(console, 'error')
.mockImplementationOnce(() => {})
wrapper = mountSearchInput()
const inputField = wrapper.find(inputSelector)
await inputField.setValue('orga')
await localVue.nextTick()
expect(consoleMock).toHaveBeenCalledWith('could not process workpackage data')
consoleMock.mockRestore()
axiosSpy.mockRestore()
})
})

describe('search list', () => {
Expand Down Expand Up @@ -212,7 +230,7 @@ describe('SearchInput.vue tests', () => {
})
})

function mountSearchInput(fileInfo = null) {
function mountSearchInput(fileInfo = { }) {
return mount(SearchInput, {
localVue,
mocks: {
Expand Down
Loading