chore: better service method types based on paths object #3267

Closed
WofWca wants to merge 1 commits from WofWca/frontend:type-safer-service-methods into main
35 changed files with 336 additions and 215 deletions

View File

@ -6,7 +6,7 @@ import AbstractModel from '@/models/abstractModel'
import type {IAbstract} from '@/modelTypes/IAbstract'
import type {Right} from '@/constants/rights'
interface Paths {
interface PathsAll {
konrad marked this conversation as resolved
Review

Why not call it AllPaths?

Why not call it `AllPaths`?
Review

Idk, "the most important part at the start", like naming Vue components.

Idk, "the most important part at the start", like naming Vue components.
create : string
get : string
getAll : string
@ -14,6 +14,7 @@ interface Paths {
delete : string
reset?: string
}
type PathsGeneric = Partial<PathsAll>;
Review

Since PathsAll is only used here couldn't we make all entries optional instead?

Since `PathsAll` is only used here couldn't we make all entries optional instead?
function convertObject(o: Record<string, unknown>) {
if (o instanceof Date) {
@ -40,7 +41,39 @@ function prepareParams(params: Record<string, unknown | unknown[]>) {
return objectToSnakeCase(params)
}
export default abstract class AbstractService<Model extends IAbstract = IAbstract> {
type VarsFromPathTemplate<P extends string> = P extends `${string}{${infer FirstKey}}${infer Rest}`
? [FirstKey, ...VarsFromPathTemplate<Rest>]
: [];
/**
* @example
* type T = ReplacerDictFromPath<'/teams/{teamId}/members/{username}'>;
* const dict: T = {
dpschen marked this conversation as resolved
Review

Please don't use single character var names.

Please don't use single character var names.
Review

Come on, it's just an example. What would you call it?

Come on, it's just an example. What would you call it?
Review

I know it seems picky, but when I read it I had to think for a bit until I understood. So Especially because it's an example it should be clear. How about params?

I know it seems picky, but when I read it I had to think for a bit until I understood. So Especially because it's an example it should be clear. How about params?
Review

Hope "dict" is good, to align with the type name.

Hope "dict" is good, to align with the type name.
Review

Perfect :)

Perfect :)
* teamId: 1,
* username: 'demo',
* }
*/
type ReplacerDictFromPath<P extends string> = Record<
VarsFromPathTemplate<P>[number],
string | number
>
type ReplacerDictOrNeverFromPath<P extends string | undefined | null> =
P extends string
? ReplacerDictFromPath<P>
: never;
/**
* Prohibit the use of `string` as values, i.e. require a more concrete type:
* string literal or template literal.
*/
type EnsureConst<T extends Record<string, string>> = {
[P in keyof T]: string extends T[P] ? never : T[P];
};
export default abstract class AbstractService<
Model extends IAbstract = IAbstract,
// Not sure if the default value makes sense here. Put it only because a non-optional
// param can't go after an optional one.
P extends PathsGeneric = EnsureConst<PathsGeneric>,
> {
/////////////////////////////
// Initial variable definitions
@ -49,13 +82,7 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
http
loading = false
uploadProgress = 0
paths: Paths = {
create: '',
get: '',
getAll: '',
update: '',
delete: '',
}
paths: P
// This contains the total number of pages and the number of results for the current page
totalPages = 0
resultCount = 0
@ -68,7 +95,7 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
* The abstract constructor.
* @param [paths] An object with all paths.
*/
constructor(paths : Partial<Paths> = {}) {
constructor(paths : EnsureConst<P> = ({} as const)) {
this.http = AuthenticatedHTTPFactory()
// Set the interceptors to process every request
@ -96,7 +123,7 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
return config
})
Object.assign(this.paths, paths)
this.paths = { ...paths } as P
}
/**
@ -267,8 +294,13 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
* @param model The model to use. The request path is built using the values from the model.
* @param params Optional query parameters
*/
get(model : Model, params = {}) {
if (this.paths.get === '') {
// TODO refactor: if `paths.get` is undefined, it's better if `get()` method
// is undefined as well, instead of just returning `never`.
get<T extends P['get']>(
model : ReplacerDictOrNeverFromPath<T>,
params = {},
): Promise<T extends string ? Model : never> {
if (!this.paths.get) {
throw new Error('This model is not able to get data.')
}
@ -279,7 +311,11 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
* This is a more abstract implementation which only does a get request.
* Services which need more flexibility can use this.
*/
async getM(url : string, model : Model = new AbstractModel({}), params: Record<string, unknown> = {}) {
async getM<T extends string>(
url : T,
model : ReplacerDictFromPath<T>,
params: Record<string, unknown> = {},
) {
const cancel = this.setLoading()
model = this.beforeGet(model)
@ -312,8 +348,14 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
* @param params Optional query parameters
* @param page The page to get
*/
async getAll(model : Model = new AbstractModel({}), params = {}, page = 1): Promise<Model[]> {
if (this.paths.getAll === '') {
// TODO refactor: ensure that if `ReplacerDict` is an empty object type then you
// can't pass an arbitrary object literal as `model`.
async getAll<T extends P['getAll']>(
model : ReplacerDictOrNeverFromPath<T> = new AbstractModel({}),
params = {},
page = 1,
): Promise<T extends string ? Model[] : never> {
if (!this.paths.getAll) {
throw new Error('This model is not able to get data.')
}
@ -342,8 +384,10 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
* Performs a put request to the url specified before
* @returns {Promise<any | never>}
*/
async create(model : Model) {
if (this.paths.create === '') {
async create<T extends P['create']>(
model : Model & ReplacerDictOrNeverFromPath<T>,
): Promise<T extends string ? Model : never> {
if (!this.paths.create) {
throw new Error('This model is not able to create data.')
}
@ -384,8 +428,10 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
/**
* Performs a post request to the update url
*/
update(model : Model) {
if (this.paths.update === '') {
update<T extends P['update']>(
model : Model & ReplacerDictOrNeverFromPath<T>,
): Promise<T extends string ? Model : never> {
if (!this.paths.update) {
throw new Error('This model is not able to update data.')
}
@ -396,8 +442,10 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
/**
* Performs a delete request to the update url
*/
async delete(model : Model) {
if (this.paths.delete === '') {
async delete<T extends P['delete']>(
model : ReplacerDictOrNeverFromPath<T>,
) {
if (!this.paths.delete) {
throw new Error('This model is not able to delete data.')
}

View File

@ -5,13 +5,15 @@ import type { IAttachment } from '@/modelTypes/IAttachment'
import {downloadBlob} from '@/helpers/downloadBlob'
export default class AttachmentService extends AbstractService<IAttachment> {
const paths = {
create: '/tasks/{taskId}/attachments',
getAll: '/tasks/{taskId}/attachments',
delete: '/tasks/{taskId}/attachments/{id}',
} as const
export default class AttachmentService extends AbstractService<IAttachment, typeof paths> {
constructor() {
super({
create: '/tasks/{taskId}/attachments',
getAll: '/tasks/{taskId}/attachments',
delete: '/tasks/{taskId}/attachments/{id}',
})
super(paths)
}
processModel(model: IAttachment) {

View File

@ -2,13 +2,15 @@ import AbstractService from './abstractService'
import AvatarModel from '@/models/avatar'
import type { IAvatar } from '@/modelTypes/IAvatar'
export default class AvatarService extends AbstractService<IAvatar> {
const paths = {
get: '/user/settings/avatar',
update: '/user/settings/avatar',
create: '/user/settings/avatar/upload',
} as const
Review

Why the const .. as const? Isn't that duplicated?

Why the `const .. as const`? Isn't that duplicated?
Review

Nope, otherwise it says that the type of get, getAll, create are strings, not string literals.

Nope, otherwise it says that the type of `get`, `getAll`, `create` are `string`s, not string literals.
export default class AvatarService extends AbstractService<IAvatar, typeof paths> {
constructor() {
super({
get: '/user/settings/avatar',
update: '/user/settings/avatar',
create: '/user/settings/avatar/upload',
})
super(paths)
}
modelFactory(data: Partial<IAvatar>) {

View File

@ -3,12 +3,14 @@ import BackgroundImageModel from '../models/backgroundImage'
import ProjectModel from '@/models/project'
import type { IBackgroundImage } from '@/modelTypes/IBackgroundImage'
export default class BackgroundUnsplashService extends AbstractService<IBackgroundImage> {
const paths = {
getAll: '/backgrounds/unsplash/search',
update: '/projects/{projectId}/backgrounds/unsplash',
} as const
export default class BackgroundUnsplashService extends AbstractService<IBackgroundImage, typeof paths> {
constructor() {
super({
getAll: '/backgrounds/unsplash/search',
update: '/projects/{projectId}/backgrounds/unsplash',
})
super(paths)
}
modelFactory(data: Partial<IBackgroundImage>) {

View File

@ -4,11 +4,13 @@ import ProjectModel from '@/models/project'
import type { IProject } from '@/modelTypes/IProject'
import type { IFile } from '@/modelTypes/IFile'
export default class BackgroundUploadService extends AbstractService {
const paths = {
create: '/projects/{projectId}/backgrounds/upload',
} as const
export default class BackgroundUploadService extends AbstractService<IProject, typeof paths> {
constructor() {
super({
create: '/projects/{projectId}/backgrounds/upload',
})
super(paths)
}
useCreateInterceptor() {

View File

@ -3,14 +3,16 @@ import BucketModel from '../models/bucket'
import TaskService from '@/services/task'
import type { IBucket } from '@/modelTypes/IBucket'
export default class BucketService extends AbstractService<IBucket> {
const paths = {
getAll: '/projects/{projectId}/buckets',
create: '/projects/{projectId}/buckets',
update: '/projects/{projectId}/buckets/{id}',
delete: '/projects/{projectId}/buckets/{id}',
} as const
export default class BucketService extends AbstractService<IBucket, typeof paths> {
constructor() {
super({
getAll: '/projects/{projectId}/buckets',
create: '/projects/{projectId}/buckets',
update: '/projects/{projectId}/buckets/{id}',
delete: '/projects/{projectId}/buckets/{id}',
})
super(paths)
}
modelFactory(data: Partial<IBucket>) {

View File

@ -2,13 +2,15 @@ import CaldavTokenModel from '@/models/caldavToken'
import type {ICaldavToken} from '@/modelTypes/ICaldavToken'
import AbstractService from './abstractService'
export default class CaldavTokenService extends AbstractService<ICaldavToken> {
const paths = {
getAll: '/user/settings/token/caldav',
create: '/user/settings/token/caldav',
delete: '/user/settings/token/caldav/{id}',
} as const
export default class CaldavTokenService extends AbstractService<ICaldavToken, typeof paths> {
constructor() {
super({
getAll: '/user/settings/token/caldav',
create: '/user/settings/token/caldav',
delete: '/user/settings/token/caldav/{id}',
})
super(paths)
}
modelFactory(data) {

View File

@ -1,9 +1,12 @@
import type { IAbstract } from '@/modelTypes/IAbstract'
import AbstractService from './abstractService'
export default class EmailUpdateService extends AbstractService {
const paths = {
update: '/user/settings/email',
} as const
export default class EmailUpdateService extends AbstractService<IAbstract, typeof paths> {
constructor() {
super({
update: '/user/settings/email',
})
super(paths)
}
}

View File

@ -3,15 +3,17 @@ import LabelModel from '@/models/label'
import type {ILabel} from '@/modelTypes/ILabel'
import {colorFromHex} from '@/helpers/color/colorFromHex'
export default class LabelService extends AbstractService<ILabel> {
const paths = {
create: '/labels',
getAll: '/labels',
get: '/labels/{id}',
update: '/labels/{id}',
delete: '/labels/{id}',
} as const
export default class LabelService extends AbstractService<ILabel, typeof paths> {
constructor() {
super({
create: '/labels',
getAll: '/labels',
get: '/labels/{id}',
update: '/labels/{id}',
delete: '/labels/{id}',
})
super(paths)
}
processModel(label) {

View File

@ -2,13 +2,15 @@ import AbstractService from './abstractService'
import LabelTask from '@/models/labelTask'
import type {ILabelTask} from '@/modelTypes/ILabelTask'
export default class LabelTaskService extends AbstractService<ILabelTask> {
const paths = {
create: '/tasks/{taskId}/labels',
getAll: '/tasks/{taskId}/labels',
delete: '/tasks/{taskId}/labels/{labelId}',
} as const
export default class LabelTaskService extends AbstractService<ILabelTask, typeof paths> {
constructor() {
super({
create: '/tasks/{taskId}/labels',
getAll: '/tasks/{taskId}/labels',
delete: '/tasks/{taskId}/labels/{labelId}',
})
super(paths)
}
modelFactory(data) {

View File

@ -2,14 +2,16 @@ import AbstractService from './abstractService'
import LinkShareModel from '@/models/linkShare'
import type {ILinkShare} from '@/modelTypes/ILinkShare'
export default class LinkShareService extends AbstractService<ILinkShare> {
const paths = {
getAll: '/projects/{projectId}/shares',
get: '/projects/{projectId}/shares/{id}',
create: '/projects/{projectId}/shares',
delete: '/projects/{projectId}/shares/{id}',
} as const
export default class LinkShareService extends AbstractService<ILinkShare, typeof paths> {
constructor() {
super({
getAll: '/projects/{projectId}/shares',
get: '/projects/{projectId}/shares/{id}',
create: '/projects/{projectId}/shares',
delete: '/projects/{projectId}/shares/{id}',
})
super(paths)
}
modelFactory(data) {

View File

@ -2,14 +2,18 @@ import AbstractService from '../abstractService'
export type MigrationConfig = { code: string }
type Paths = {
update: `/migration/${string}/migrate`
}
// This service builds on top of the abstract service and basically just hides away method names.
// It enables migration services to be created with minimal overhead and even better method names.
export default class AbstractMigrationService extends AbstractService<MigrationConfig> {
export default class AbstractMigrationService extends AbstractService<MigrationConfig, Paths> {
serviceUrlKey = ''
constructor(serviceUrlKey: string) {
super({
update: '/migration/' + serviceUrlKey + '/migrate',
update: `/migration/${serviceUrlKey}/migrate`,
})
this.serviceUrlKey = serviceUrlKey
}

View File

@ -3,15 +3,17 @@ import NamespaceModel from '../models/namespace'
import type {INamespace} from '@/modelTypes/INamespace'
import {colorFromHex} from '@/helpers/color/colorFromHex'
export default class NamespaceService extends AbstractService<INamespace> {
const paths = {
create: '/namespaces',
get: '/namespaces/{id}',
getAll: '/namespaces',
update: '/namespaces/{id}',
delete: '/namespaces/{id}',
} as const
export default class NamespaceService extends AbstractService<INamespace, typeof paths> {
constructor() {
super({
create: '/namespaces',
get: '/namespaces/{id}',
getAll: '/namespaces',
update: '/namespaces/{id}',
delete: '/namespaces/{id}',
})
super(paths)
}
modelFactory(data) {

View File

@ -2,12 +2,14 @@ import AbstractService from '@/services/abstractService'
import NotificationModel from '@/models/notification'
import type {INotification} from '@/modelTypes/INotification'
export default class NotificationService extends AbstractService<INotification> {
const paths = {
getAll: '/notifications',
update: '/notifications/{id}',
} as const
export default class NotificationService extends AbstractService<INotification, typeof paths> {
constructor() {
super({
getAll: '/notifications',
update: '/notifications/{id}',
})
super(paths)
}
modelFactory(data) {

View File

@ -2,10 +2,11 @@ import AbstractService from './abstractService'
import PasswordResetModel from '@/models/passwordReset'
import type {IPasswordReset} from '@/modelTypes/IPasswordReset'
export default class PasswordResetService extends AbstractService<IPasswordReset> {
const paths = {} as const
export default class PasswordResetService extends AbstractService<IPasswordReset, typeof paths> {
constructor() {
super({})
super(paths)
this.paths = {
reset: '/user/password/reset',
requestReset: '/user/password/token',

View File

@ -1,10 +1,12 @@
import AbstractService from './abstractService'
import type {IPasswordUpdate} from '@/modelTypes/IPasswordUpdate'
export default class PasswordUpdateService extends AbstractService<IPasswordUpdate> {
const paths = {
update: '/user/password',
} as const
export default class PasswordUpdateService extends AbstractService<IPasswordUpdate, typeof paths> {
constructor() {
super({
update: '/user/password',
})
super(paths)
}
}

View File

@ -4,15 +4,17 @@ import type {IProject} from '@/modelTypes/IProject'
import TaskService from './task'
import {colorFromHex} from '@/helpers/color/colorFromHex'
export default class ProjectService extends AbstractService<IProject> {
const paths = {
create: '/namespaces/{namespaceId}/projects',
get: '/projects/{id}',
getAll: '/projects',
update: '/projects/{id}',
delete: '/projects/{id}',
} as const
export default class ProjectService extends AbstractService<IProject, typeof paths> {
constructor() {
super({
create: '/namespaces/{namespaceId}/projects',
get: '/projects/{id}',
getAll: '/projects',
update: '/projects/{id}',
delete: '/projects/{id}',
})
super(paths)
}
modelFactory(data) {

View File

@ -2,11 +2,13 @@ import AbstractService from './abstractService'
import projectDuplicateModel from '@/models/projectDuplicateModel'
import type {IProjectDuplicate} from '@/modelTypes/IProjectDuplicate'
export default class ProjectDuplicateService extends AbstractService<IProjectDuplicate> {
const paths = {
create: '/projects/{projectId}/duplicate',
} as const
export default class ProjectDuplicateService extends AbstractService<IProjectDuplicate, typeof paths> {
constructor() {
super({
create: '/projects/{projectId}/duplicate',
})
super(paths)
}
beforeCreate(model) {

View File

@ -1,11 +1,13 @@
import AbstractService from './abstractService'
import UserModel from '../models/user'
export default class ProjectUserService extends AbstractService {
const paths = {
getAll: '/projects/{projectId}/projectusers',
} as const
export default class ProjectUserService extends AbstractService<UserModel, typeof paths> {
constructor() {
super({
getAll: '/projects/{projectId}/projectusers',
})
super(paths)
}
modelFactory(data) {

View File

@ -43,14 +43,16 @@ export function isSavedFilter(project: IProject) {
return getSavedFilterIdFromProjectId(project.id) > 0
}
export default class SavedFilterService extends AbstractService<ISavedFilter> {
const paths = {
get: '/filters/{id}',
create: '/filters',
update: '/filters/{id}',
delete: '/filters/{id}',
} as const
export default class SavedFilterService extends AbstractService<ISavedFilter, typeof paths> {
constructor() {
super({
get: '/filters/{id}',
create: '/filters',
update: '/filters/{id}',
delete: '/filters/{id}',
})
super(paths)
}
modelFactory(data) {

View File

@ -2,12 +2,14 @@ import AbstractService from '@/services/abstractService'
import SubscriptionModel from '@/models/subscription'
import type {ISubscription} from '@/modelTypes/ISubscription'
export default class SubscriptionService extends AbstractService<ISubscription> {
const paths = {
create: '/subscriptions/{entity}/{entityId}',
delete: '/subscriptions/{entity}/{entityId}',
} as const
export default class SubscriptionService extends AbstractService<ISubscription, typeof paths> {
constructor() {
super({
create: '/subscriptions/{entity}/{entityId}',
delete: '/subscriptions/{entity}/{entityId}',
})
super(paths)
}
modelFactory(data) {

View File

@ -15,15 +15,17 @@ const parseDate = date => {
return null
}
export default class TaskService extends AbstractService<ITask> {
const paths = {
create: '/projects/{projectId}',
getAll: '/tasks/all',
get: '/tasks/{id}',
update: '/tasks/{id}',
delete: '/tasks/{id}',
} as const
export default class TaskService extends AbstractService<ITask, typeof paths> {
constructor() {
super({
create: '/projects/{projectId}',
getAll: '/tasks/all',
get: '/tasks/{id}',
update: '/tasks/{id}',
delete: '/tasks/{id}',
})
super(paths)
}
modelFactory(data) {

View File

@ -2,12 +2,14 @@ import AbstractService from './abstractService'
import TaskAssigneeModel from '@/models/taskAssignee'
import type {ITaskAssignee} from '@/modelTypes/ITaskAssignee'
export default class TaskAssigneeService extends AbstractService<ITaskAssignee> {
const paths = {
create: '/tasks/{taskId}/assignees',
delete: '/tasks/{taskId}/assignees/{userId}',
} as const
export default class TaskAssigneeService extends AbstractService<ITaskAssignee, typeof paths> {
constructor() {
super({
create: '/tasks/{taskId}/assignees',
delete: '/tasks/{taskId}/assignees/{userId}',
})
super(paths)
}
modelFactory(data) {

View File

@ -14,11 +14,13 @@ export interface GetAllTasksParams {
filter_include_nulls: boolean,
}
export default class TaskCollectionService extends AbstractService<ITask> {
const paths = {
getAll: '/projects/{projectId}/tasks',
} as const
export default class TaskCollectionService extends AbstractService<ITask, typeof paths> {
constructor() {
super({
getAll: '/projects/{projectId}/tasks',
})
super(paths)
}
modelFactory(data) {

View File

@ -2,15 +2,17 @@ import AbstractService from './abstractService'
import TaskCommentModel from '@/models/taskComment'
import type {ITaskComment} from '@/modelTypes/ITaskComment'
export default class TaskCommentService extends AbstractService<ITaskComment> {
const paths = {
create: '/tasks/{taskId}/comments',
getAll: '/tasks/{taskId}/comments',
get: '/tasks/{taskId}/comments/{id}',
update: '/tasks/{taskId}/comments/{id}',
delete: '/tasks/{taskId}/comments/{id}',
} as const
export default class TaskCommentService extends AbstractService<ITaskComment, typeof paths> {
constructor() {
super({
create: '/tasks/{taskId}/comments',
getAll: '/tasks/{taskId}/comments',
get: '/tasks/{taskId}/comments/{id}',
update: '/tasks/{taskId}/comments/{id}',
delete: '/tasks/{taskId}/comments/{id}',
})
super(paths)
}
modelFactory(data) {

View File

@ -2,12 +2,14 @@ import AbstractService from './abstractService'
import TaskRelationModel from '@/models/taskRelation'
import type {ITaskRelation} from '@/modelTypes/ITaskRelation'
export default class TaskRelationService extends AbstractService<ITaskRelation> {
const paths = {
create: '/tasks/{taskId}/relations',
delete: '/tasks/{taskId}/relations/{relationKind}/{otherTaskId}',
} as const
export default class TaskRelationService extends AbstractService<ITaskRelation, typeof paths> {
constructor() {
super({
create: '/tasks/{taskId}/relations',
delete: '/tasks/{taskId}/relations/{relationKind}/{otherTaskId}',
})
super(paths)
}
modelFactory(data) {

View File

@ -2,15 +2,17 @@ import AbstractService from './abstractService'
import TeamModel from '@/models/team'
import type {ITeam} from '@/modelTypes/ITeam'
export default class TeamService extends AbstractService<ITeam> {
const paths = {
create: '/teams',
get: '/teams/{id}',
getAll: '/teams',
update: '/teams/{id}',
delete: '/teams/{id}',
} as const
export default class TeamService extends AbstractService<ITeam, typeof paths> {
constructor() {
super({
create: '/teams',
get: '/teams/{id}',
getAll: '/teams',
update: '/teams/{id}',
delete: '/teams/{id}',
})
super(paths)
}
modelFactory(data) {

View File

@ -2,13 +2,15 @@ import AbstractService from './abstractService'
import TeamMemberModel from '@/models/teamMember'
import type {ITeamMember} from '@/modelTypes/ITeamMember'
export default class TeamMemberService extends AbstractService<ITeamMember> {
const paths = {
create: '/teams/{teamId}/members',
delete: '/teams/{teamId}/members/{username}',
update: '/teams/{teamId}/members/{username}/admin',
} as const
export default class TeamMemberService extends AbstractService<ITeamMember, typeof paths> {
constructor() {
super({
create: '/teams/{teamId}/members',
delete: '/teams/{teamId}/members/{username}',
update: '/teams/{teamId}/members/{username}/admin',
})
super(paths)
}
modelFactory(data) {

View File

@ -3,14 +3,16 @@ import TeamNamespaceModel from '@/models/teamNamespace'
import type {ITeamNamespace} from '@/modelTypes/ITeamNamespace'
import TeamModel from '@/models/team'
export default class TeamNamespaceService extends AbstractService<ITeamNamespace> {
const paths = {
create: '/namespaces/{namespaceId}/teams',
getAll: '/namespaces/{namespaceId}/teams',
update: '/namespaces/{namespaceId}/teams/{teamId}',
delete: '/namespaces/{namespaceId}/teams/{teamId}',
} as const
export default class TeamNamespaceService extends AbstractService<ITeamNamespace, typeof paths> {
constructor() {
super({
create: '/namespaces/{namespaceId}/teams',
getAll: '/namespaces/{namespaceId}/teams',
update: '/namespaces/{namespaceId}/teams/{teamId}',
delete: '/namespaces/{namespaceId}/teams/{teamId}',
})
super(paths)
}
modelFactory(data) {

View File

@ -3,14 +3,16 @@ import TeamProjectModel from '@/models/teamProject'
import type {ITeamProject} from '@/modelTypes/ITeamProject'
import TeamModel from '@/models/team'
export default class TeamProjectService extends AbstractService<ITeamProject> {
const paths = {
create: '/projects/{projectId}/teams',
getAll: '/projects/{projectId}/teams',
update: '/projects/{projectId}/teams/{teamId}',
delete: '/projects/{projectId}/teams/{teamId}',
} as const
export default class TeamProjectService extends AbstractService<ITeamProject, typeof paths> {
constructor() {
super({
create: '/projects/{projectId}/teams',
getAll: '/projects/{projectId}/teams',
update: '/projects/{projectId}/teams/{teamId}',
delete: '/projects/{projectId}/teams/{teamId}',
})
super(paths)
}
modelFactory(data) {

View File

@ -2,13 +2,18 @@ import AbstractService from './abstractService'
import TotpModel from '@/models/totp'
import type {ITotp} from '@/modelTypes/ITotp'
export default class TotpService extends AbstractService<ITotp> {
urlPrefix = '/user/settings/totp'
const urlPrefix = '/user/settings/totp' as const
type Paths = {
get: typeof urlPrefix
}
export default class TotpService extends AbstractService<ITotp, Paths> {
urlPrefix = urlPrefix
constructor() {
super({})
this.paths.get = this.urlPrefix
super({
get: urlPrefix,
})
}
modelFactory(data) {

View File

@ -2,11 +2,13 @@ import AbstractService from './abstractService'
import UserModel from '@/models/user'
import type {IUser} from '@/modelTypes/IUser'
export default class UserService extends AbstractService<IUser> {
const paths = {
getAll: '/users',
} as const
export default class UserService extends AbstractService<IUser, typeof paths> {
constructor() {
super({
getAll: '/users',
})
super(paths)
}
modelFactory(data) {

View File

@ -3,14 +3,16 @@ import UserNamespaceModel from '@/models/userNamespace'
import type {IUserNamespace} from '@/modelTypes/IUserNamespace'
import UserModel from '@/models/user'
export default class UserNamespaceService extends AbstractService<IUserNamespace> {
const paths = {
create: '/namespaces/{namespaceId}/users',
getAll: '/namespaces/{namespaceId}/users',
update: '/namespaces/{namespaceId}/users/{userId}',
delete: '/namespaces/{namespaceId}/users/{userId}',
} as const
export default class UserNamespaceService extends AbstractService<IUserNamespace, typeof paths> {
constructor() {
super({
create: '/namespaces/{namespaceId}/users',
getAll: '/namespaces/{namespaceId}/users',
update: '/namespaces/{namespaceId}/users/{userId}',
delete: '/namespaces/{namespaceId}/users/{userId}',
})
super(paths)
}
modelFactory(data) {

View File

@ -3,14 +3,16 @@ import UserProjectModel from '@/models/userProject'
import type {IUserProject} from '@/modelTypes/IUserProject'
import UserModel from '@/models/user'
export default class UserProjectService extends AbstractService<IUserProject> {
const paths = {
create: '/projects/{projectId}/users',
getAll: '/projects/{projectId}/users',
update: '/projects/{projectId}/users/{userId}',
delete: '/projects/{projectId}/users/{userId}',
} as const
export default class UserProjectService extends AbstractService<IUserProject, typeof paths> {
constructor() {
super({
create: '/projects/{projectId}/users',
getAll: '/projects/{projectId}/users',
update: '/projects/{projectId}/users/{userId}',
delete: '/projects/{projectId}/users/{userId}',
})
super(paths)
}
modelFactory(data) {

View File

@ -1,10 +1,12 @@
import type {IUserSettings} from '@/modelTypes/IUserSettings'
import AbstractService from './abstractService'
export default class UserSettingsService extends AbstractService<IUserSettings> {
const paths = {
update: '/user/settings/general',
} as const
export default class UserSettingsService extends AbstractService<IUserSettings, typeof paths> {
constructor() {
super({
update: '/user/settings/general',
})
super(paths)
}
}