1
0

Make all api fields snake_case (#105)

Change all snake/camelCase mix and match to camelCase everywhere

Fix conversion to not interfer with service interceptors

Add dynamic conversion between camelCase and snake_case to services

Co-authored-by: kolaente <k@knt.li>
Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/105
This commit is contained in:
konrad
2020-04-12 21:54:46 +00:00
parent de36296bac
commit 4a413e7f3c
60 changed files with 296 additions and 189 deletions

View File

@ -1,5 +1,7 @@
import axios from 'axios'
import {reduce, replace} from 'lodash'
import {camelCase} from 'camel-case'
import {snakeCase} from 'snake-case'
let config = require('../../public/config.json')
@ -40,19 +42,25 @@ export default class AbstractService {
// Set the interceptors to process every request
let self = this
this.http.interceptors.request.use( (config) => {
this.http.interceptors.request.use((config) => {
switch (config.method) {
case 'post':
if(this.useUpdateInterceptor())
config.data = JSON.stringify(self.beforeUpdate(config.data))
if (this.useUpdateInterceptor()) {
config.data = self.beforeUpdate(config.data)
}
config.data = JSON.stringify(this.modelToSnakeCase(config.data))
break
case 'put':
if(this.useCreateInterceptor())
config.data = JSON.stringify(self.beforeCreate(config.data))
if (this.useCreateInterceptor()) {
config.data = self.beforeCreate(config.data)
}
config.data = JSON.stringify(this.modelToSnakeCase(config.data))
break
case 'delete':
if(this.useDeleteInterceptor())
config.data = JSON.stringify(self.beforeDelete(config.data))
if (this.useDeleteInterceptor()) {
config.data = self.beforeDelete(config.data)
}
config.data = JSON.stringify(this.modelToSnakeCase(config.data))
break
}
return config
@ -66,7 +74,7 @@ export default class AbstractService {
) {
this.http.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('token')
}
this.paths = {
create: paths.create !== undefined ? paths.create : '',
get: paths.get !== undefined ? paths.get : '',
@ -112,7 +120,7 @@ export default class AbstractService {
errorHandler(error) {
return Promise.reject(error)
}
/////////////////
// Helper functions
///////////////
@ -231,6 +239,32 @@ export default class AbstractService {
// Preprocessors
////////////
/**
* Transforms field names to camel case.
* @param model
* @returns {*}
*/
modelToCamelCase(model) {
let parsedModel = {}
for (const m in model) {
parsedModel[camelCase(m)] = model[m]
}
return parsedModel
}
/**
* Transforms field names to snake case - used before making an api request.
* @param model
* @returns {*}
*/
modelToSnakeCase(model) {
let parsedModel = {}
for (const m in model) {
parsedModel[snakeCase(m)] = model[m]
}
return parsedModel
}
/**
* Default preprocessor for get requests
* @param model
@ -295,12 +329,16 @@ export default class AbstractService {
*/
getM(url, model = {}, params = {}) {
const cancel = this.setLoading()
model = this.beforeGet(model)
return this.http.get(this.getReplacedRoute(url, model), {params: params})
const finalUrl = this.getReplacedRoute(url, model)
return this.http.get(finalUrl, {params: params})
.catch(error => {
return this.errorHandler(error)
})
.then(response => {
response.data = this.modelToCamelCase(response.data)
return Promise.resolve(this.modelGetFactory(response.data))
})
.finally(() => {
@ -325,7 +363,9 @@ export default class AbstractService {
const cancel = this.setLoading()
model = this.beforeGet(model)
return this.http.get(this.getReplacedRoute(this.paths.getAll, model), {params: params})
const finalUrl = this.getReplacedRoute(this.paths.getAll, model)
return this.http.get(finalUrl, {params: params})
.catch(error => {
return this.errorHandler(error)
})
@ -338,16 +378,17 @@ export default class AbstractService {
return this.modelGetAllFactory(entry)
}))
}
if(response.data === null) {
if (response.data === null) {
return Promise.resolve([])
}
response.data = this.modelToCamelCase(response.data)
return Promise.resolve(this.modelGetAllFactory(response.data))
})
.finally(() => {
cancel()
})
}
/**
* Performs a put request to the url specified before
* @param model
@ -359,18 +400,21 @@ export default class AbstractService {
}
const cancel = this.setLoading()
return this.http.put(this.getReplacedRoute(this.paths.create, model), model)
const finalUrl = this.getReplacedRoute(this.paths.create, model)
return this.http.put(finalUrl, model)
.catch(error => {
return this.errorHandler(error)
})
.then(response => {
response.data = this.modelToCamelCase(response.data)
return Promise.resolve(this.modelCreateFactory(response.data))
})
.finally(() => {
cancel()
})
}
/**
* Performs a post request to the update url
* @param model
@ -382,11 +426,14 @@ export default class AbstractService {
}
const cancel = this.setLoading()
return this.http.post(this.getReplacedRoute(this.paths.update, model), model)
const finalUrl = this.getReplacedRoute(this.paths.update, model)
return this.http.post(finalUrl, model)
.catch(error => {
return this.errorHandler(error)
})
.then(response => {
response.data = this.modelToCamelCase(response.data)
return Promise.resolve(this.modelUpdateFactory(response.data))
})
.finally(() => {
@ -405,11 +452,14 @@ export default class AbstractService {
}
const cancel = this.setLoading()
return this.http.delete(this.getReplacedRoute(this.paths.delete, model), model)
const finalUrl = this.getReplacedRoute(this.paths.delete, model)
return this.http.delete(finalUrl, model)
.catch(error => {
return this.errorHandler(error)
})
.then(response => {
response.data = this.modelToCamelCase(response.data)
return Promise.resolve(response.data)
})
.finally(() => {

View File

@ -5,9 +5,9 @@ import {formatISO} from 'date-fns'
export default class AttachmentService extends AbstractService {
constructor() {
super({
create: '/tasks/{task_id}/attachments',
getAll: '/tasks/{task_id}/attachments',
delete: '/tasks/{task_id}/attachments/{id}',
create: '/tasks/{taskId}/attachments',
getAll: '/tasks/{taskId}/attachments',
delete: '/tasks/{taskId}/attachments/{id}',
})
}
@ -36,7 +36,7 @@ export default class AttachmentService extends AbstractService {
download(model) {
this.http({
url: '/tasks/' + model.task_id + '/attachments/' + model.id,
url: '/tasks/' + model.taskId + '/attachments/' + model.id,
method: 'GET',
responseType: 'blob',
}).then((response) => {

View File

@ -24,12 +24,12 @@ export default class LabelService extends AbstractService {
}
beforeUpdate(label) {
label.hex_color = label.hex_color.substring(1, 7)
label.hexColor = label.hexColor.substring(1, 7)
return label
}
beforeCreate(label) {
label.hex_color = label.hex_color.substring(1, 7)
label.hexColor = label.hexColor.substring(1, 7)
return label
}
}

View File

@ -6,7 +6,7 @@ export default class LabelTaskService extends AbstractService {
super({
create: '/tasks/{taskID}/labels',
getAll: '/tasks/{taskID}/labels',
delete: '/tasks/{taskID}/labels/{label_id}',
delete: '/tasks/{taskID}/labels/{labelId}',
})
}

View File

@ -5,10 +5,10 @@ import {formatISO} from 'date-fns'
export default class ListService extends AbstractService {
constructor() {
super({
getAll: '/lists/{listID}/shares',
get: '/lists/{listID}/shares/{id}',
create: '/lists/{listID}/shares',
delete: '/lists/{listID}/shares/{id}',
getAll: '/lists/{listId}/shares',
get: '/lists/{listId}/shares/{id}',
create: '/lists/{listId}/shares',
delete: '/lists/{listId}/shares/{id}',
})
}

View File

@ -28,12 +28,12 @@ export default class ListService extends AbstractService {
model.tasks = model.tasks.map(task => {
return taskService.beforeUpdate(task)
})
model.hex_color = model.hex_color.substring(1, 7)
model.hexColor = model.hexColor.substring(1, 7)
return model
}
beforeCreate(list) {
list.hex_color = list.hex_color.substring(1, 7)
list.hexColor = list.hexColor.substring(1, 7)
return list
}
}

View File

@ -5,7 +5,7 @@ import {formatISO} from 'date-fns'
export default class ListUserService extends AbstractService {
constructor() {
super({
getAll: '/lists/{listID}/listusers'
getAll: '/lists/{listId}/listusers'
})
}

View File

@ -24,12 +24,12 @@ export default class NamespaceService extends AbstractService {
}
beforeUpdate(namespace) {
namespace.hex_color = namespace.hex_color.substring(1, 7)
namespace.hexColor = namespace.hexColor.substring(1, 7)
return namespace
}
beforeCreate(namespace) {
namespace.hex_color = namespace.hex_color.substring(1, 7)
namespace.hexColor = namespace.hexColor.substring(1, 7)
return namespace
}
}

View File

@ -6,7 +6,7 @@ import {formatISO} from 'date-fns'
export default class TaskService extends AbstractService {
constructor() {
super({
create: '/lists/{listID}',
create: '/lists/{listId}',
getAll: '/tasks/all',
get: '/tasks/{id}',
update: '/tasks/{id}',
@ -27,8 +27,11 @@ export default class TaskService extends AbstractService {
}
processModel(model) {
// Ensure the listID is an int
model.listID = Number(model.listID)
console.log(model)
// Ensure that listId is an int
model.listId = Number(model.listId)
// Convert dates into an iso string
model.dueDate = model.dueDate === null ? null : formatISO(new Date(model.dueDate))
@ -79,8 +82,8 @@ export default class TaskService extends AbstractService {
}
// Do the same for all related tasks
Object.keys(model.related_tasks).forEach(relationKind => {
model.related_tasks[relationKind] = model.related_tasks[relationKind].map(t => {
Object.keys(model.relatedTasks).forEach(relationKind => {
model.relatedTasks[relationKind] = model.relatedTasks[relationKind].map(t => {
return this.processModel(t)
})
})

View File

@ -5,8 +5,8 @@ import {formatISO} from 'date-fns'
export default class TaskAssigneeService extends AbstractService {
constructor() {
super({
create: '/tasks/{task_id}/assignees',
delete: '/tasks/{task_id}/assignees/{user_id}',
create: '/tasks/{taskId}/assignees',
delete: '/tasks/{taskId}/assignees/{userId}',
})
}

View File

@ -5,7 +5,7 @@ import {formatISO} from 'date-fns'
export default class TaskCollectionService extends AbstractService {
constructor() {
super({
getAll: '/lists/{listID}/tasks',
getAll: '/lists/{listId}/tasks',
})
}

View File

@ -5,11 +5,11 @@ import {formatISO} from 'date-fns'
export default class TaskCommentService extends AbstractService {
constructor() {
super({
create: '/tasks/{task_id}/comments',
getAll: '/tasks/{task_id}/comments',
get: '/tasks/{task_id}/comments/{id}',
update: '/tasks/{task_id}/comments/{id}',
delete: '/tasks/{task_id}/comments/{id}',
create: '/tasks/{taskId}/comments',
getAll: '/tasks/{taskId}/comments',
get: '/tasks/{taskId}/comments/{id}',
update: '/tasks/{taskId}/comments/{id}',
delete: '/tasks/{taskId}/comments/{id}',
})
}

View File

@ -5,8 +5,8 @@ import {formatISO} from 'date-fns'
export default class TaskRelationService extends AbstractService {
constructor() {
super({
create: '/tasks/{task_id}/relations',
delete: '/tasks/{task_id}/relations',
create: '/tasks/{taskId}/relations',
delete: '/tasks/{taskId}/relations',
})
}

View File

@ -6,10 +6,10 @@ import {formatISO} from 'date-fns'
export default class TeamListService extends AbstractService {
constructor() {
super({
create: '/lists/{listID}/teams',
getAll: '/lists/{listID}/teams',
update: '/lists/{listID}/teams/{teamID}',
delete: '/lists/{listID}/teams/{teamID}',
create: '/lists/{listId}/teams',
getAll: '/lists/{listId}/teams',
update: '/lists/{listId}/teams/{teamId}',
delete: '/lists/{listId}/teams/{teamId}',
})
}

View File

@ -5,8 +5,8 @@ import {formatISO} from 'date-fns'
export default class TeamMemberService extends AbstractService {
constructor() {
super({
create: '/teams/{teamID}/members',
delete: '/teams/{teamID}/members/{id}', // "id" is the user id because we're intheriting from a normal user
create: '/teams/{teamId}/members',
delete: '/teams/{teamId}/members/{id}', // "id" is the user id because we're intheriting from a normal user
});
}
@ -21,7 +21,7 @@ export default class TeamMemberService extends AbstractService {
}
beforeCreate(model) {
model.userID = model.id // The api wants to get the user id as userID
model.userId = model.id // The api wants to get the user id as user_ID
model.admin = model.admin === null ? false : model.admin
return model
}

View File

@ -8,8 +8,8 @@ export default class TeamNamespaceService extends AbstractService {
super({
create: '/namespaces/{namespaceID}/teams',
getAll: '/namespaces/{namespaceID}/teams',
update: '/namespaces/{namespaceID}/teams/{teamID}',
delete: '/namespaces/{namespaceID}/teams/{teamID}',
update: '/namespaces/{namespaceID}/teams/{teamId}',
delete: '/namespaces/{namespaceID}/teams/{teamId}',
})
}

View File

@ -6,10 +6,10 @@ import {formatISO} from 'date-fns'
export default class UserListService extends AbstractService {
constructor() {
super({
create: '/lists/{listID}/users',
getAll: '/lists/{listID}/users',
update: '/lists/{listID}/users/{userID}',
delete: '/lists/{listID}/users/{userID}',
create: '/lists/{listId}/users',
getAll: '/lists/{listId}/users',
update: '/lists/{listId}/users/{userId}',
delete: '/lists/{listId}/users/{userId}',
})
}

View File

@ -8,8 +8,8 @@ export default class UserNamespaceService extends AbstractService {
super({
create: '/namespaces/{namespaceID}/users',
getAll: '/namespaces/{namespaceID}/users',
update: '/namespaces/{namespaceID}/users/{userID}',
delete: '/namespaces/{namespaceID}/users/{userID}',
update: '/namespaces/{namespaceID}/users/{userId}',
delete: '/namespaces/{namespaceID}/users/{userId}',
})
}