Browse Source

Rename link to relation

pull/1703/head
Hironsan 2 years ago
parent
commit
2465e5698c
19 changed files with 115 additions and 642 deletions
  1. 33
      frontend/components/links/ActionMenu.vue
  2. 95
      frontend/components/links/FormCreate.vue
  3. 28
      frontend/components/links/FormDelete.vue
  4. 91
      frontend/components/links/LinksList.vue
  5. 48
      frontend/domain/models/links/link.ts
  6. 13
      frontend/domain/models/links/linkRepository.ts
  7. 11
      frontend/domain/models/links/linkTypesRepository.ts
  8. 24
      frontend/domain/models/tasks/relation.ts
  9. 13
      frontend/domain/models/tasks/relationRepository.ts
  10. 162
      frontend/pages/projects/_id/links/index.vue
  11. 10
      frontend/pages/projects/_id/sequence-labeling/index.vue
  12. 4
      frontend/plugins/services.ts
  13. 38
      frontend/repositories/links/apiLinkRepository.ts
  14. 41
      frontend/repositories/links/apiLinkTypesRepository.ts
  15. 38
      frontend/repositories/tasks/sequenceLabeling/apiRelationRepository.ts
  16. 27
      frontend/services/application/links/linkData.ts
  17. 30
      frontend/services/application/links/linkTypesApplicationService.ts
  18. 15
      frontend/services/application/tasks/sequenceLabeling/relationData.ts
  19. 36
      frontend/services/application/tasks/sequenceLabeling/sequenceLabelingApplicationService.ts

33
frontend/components/links/ActionMenu.vue

@ -1,33 +0,0 @@
<template>
<action-menu
:items="items"
:text="$t('dataset.actions')"
@create="$emit('create')"
@upload="$emit('upload')"
@download="$emit('download')"
/>
</template>
<script lang="ts">
import Vue from 'vue'
import { mdiPencil } from '@mdi/js'
import ActionMenu from '~/components/utils/ActionMenu.vue'
export default Vue.extend({
components: {
ActionMenu
},
computed: {
items() {
return [
{
title: this.$t('links.createLink'),
icon: mdiPencil,
event: 'create'
}
]
}
}
})
</script>

95
frontend/components/links/FormCreate.vue

@ -1,95 +0,0 @@
<template>
<base-card
:disabled="!valid"
:title="$t('labels.createLink')"
:agree-text="$t('generic.save')"
:cancel-text="$t('generic.cancel')"
@agree="$emit('save')"
@cancel="$emit('cancel')"
>
<template #content>
<v-form v-model="valid">
<v-text-field
:value="name"
:label="$t('labels.linkName')"
:rules="[rules.required, rules.counter, rules.nameDuplicated]"
:prepend-icon="mdiLabel"
single-line
counter
autofocus
@input="updateValue('name', $event)"
/>
<v-color-picker
:value="color"
:rules="[rules.required]"
show-swatches
hide-mode-switch
width="800"
@input="updateValue('color', $event)"
/>
</v-form>
</template>
</base-card>
</template>
<script lang="ts">
import Vue from 'vue'
import { mdiLabel } from '@mdi/js'
import BaseCard from '@/components/utils/BaseCard.vue'
export default Vue.extend({
components: {
BaseCard
},
props: {
name: {
type: String,
default: '',
required: true
},
suffixKey: {
type: String,
default: null,
},
color: {
type: String,
default: '#ffffff',
required: true
},
usedNames: {
type: Array,
default: () => [],
required: true
}
},
data() {
return {
valid: false,
rules: {
required: (v: string) => !!v || 'Required',
// @ts-ignore
counter: (v: string) => (v && v.length <= 100) || this.$t('rules.labelNameRules').labelLessThan100Chars,
// @ts-ignore
nameDuplicated: (v: string) => (!this.usedNames.includes(v)) || this.$t('rules.labelNameRules').duplicated,
// @ts-ignore
keyDuplicated: (v: string) => !this.usedKeys.includes(v) || this.$t('rules.keyNameRules').duplicated,
},
mdiLabel
}
},
computed: {
shortkeys() {
return '0123456789abcdefghijklmnopqrstuvwxyz'.split('')
}
},
methods: {
updateValue(key: string, value: string) {
this.$emit(`update:${key}`, value);
}
}
})
</script>

28
frontend/components/links/FormDelete.vue

@ -1,28 +0,0 @@
<template>
<confirm-form
:items="selected"
title="Delete Label"
:message="$t('labels.deleteMessage')"
item-key="text"
@ok="$emit('remove')"
@cancel="$emit('cancel')"
/>
</template>
<script lang="ts">
import Vue from 'vue'
import ConfirmForm from '@/components/utils/ConfirmForm.vue'
export default Vue.extend({
components: {
ConfirmForm
},
props: {
selected: {
type: Array,
default: () => []
}
}
})
</script>

91
frontend/components/links/LinksList.vue

@ -1,91 +0,0 @@
<template>
<v-data-table
:value="value"
:headers="headers"
:items="items"
:search="search"
:loading="isLoading"
:loading-text="$t('generic.loading')"
:no-data-text="$t('vuetify.noDataAvailable')"
:footer-props="{
'showFirstLastPage': true,
'items-per-page-options': [5, 10, 15, $t('generic.all')],
'items-per-page-text': $t('vuetify.itemsPerPageText'),
'page-text': $t('dataset.pageText')
}"
item-key="id"
show-select
@input="$emit('input', $event)"
>
<template #top>
<v-text-field
v-model="search"
:prepend-inner-icon="mdiMagnify"
:label="$t('generic.search')"
single-line
hide-details
filled
/>
</template>
<template #[`item.color`]="props">
<v-chip
:color="props.item.color"
:text-color="$contrastColor(props.item.color)"
>
{{ props.item.color }}
</v-chip>
</template>
<template #[`item.actions`]="{ item }">
<v-icon
small
@click="$emit('edit', item)"
>
{{ mdiPencil }}
</v-icon>
</template>
</v-data-table>
</template>
<script lang="ts">
import Vue, { PropType } from 'vue'
import { mdiPencil, mdiMagnify } from '@mdi/js'
import { LinkTypeDTO } from '~/services/application/links/linkData'
export default Vue.extend({
props: {
isLoading: {
type: Boolean,
default: false,
required: true
},
items: {
type: Array as PropType<LinkTypeDTO[]>,
default: () => [],
required: true
},
value: {
type: Array as PropType<LinkTypeDTO[]>,
default: () => [],
required: true
}
},
data() {
return {
search: '',
mdiPencil,
mdiMagnify
}
},
computed: {
headers() {
return [
{ text: this.$t('generic.name'), value: 'name' },
{ text: this.$t('labels.color'), value: 'color' },
{ text: 'Actions', value: 'actions', sortable: false },
]
}
}
})
</script>

48
frontend/domain/models/links/link.ts

@ -1,48 +0,0 @@
export class LinkTypeItem {
constructor(
public id: number,
public name: string,
public color: string = '#1f1f1f'
) {
}
static valueOf(
{id, name, color}:
{ id: number, name: string, color: string }
): LinkTypeItem {
return new LinkTypeItem(id, name, color)
}
toObject(): Object {
return {
id: this.id,
name: this.name,
color: this.color
}
}
}
export class LinkItem {
constructor(
public id: number,
public fromId: number,
public toId: number,
public type: number,
) {
}
static valueOf(
{id, from_id, to_id, type}: { id: number, from_id: number, to_id: number, type: number }
): LinkItem {
return new LinkItem(id, from_id, to_id, type)
}
toObject(): Object {
return {
id: this.id,
from_id: this.fromId,
to_id: this.toId,
type: this.type,
}
}
}

13
frontend/domain/models/links/linkRepository.ts

@ -1,13 +0,0 @@
import {LinkItem} from '~/domain/models/links/link'
export interface LinkRepository {
list(projectId: string, exampleId: number): Promise<LinkItem[]>
create(projectId: string, exampleId: number, link: LinkItem): Promise<LinkItem>
update(projectId: string, exampleId: number, linkId: number, linkType: number): Promise<LinkItem>
delete(projectId: string, exampleId: number, linkId: number): Promise<void>
bulkDelete(projectId: string, exampleId: number, linkIds: number[]): Promise<void>
}

11
frontend/domain/models/links/linkTypesRepository.ts

@ -1,11 +0,0 @@
import { LinkTypeItem } from '~/domain/models/links/link'
export interface LinkTypesRepository {
list(projectId: string): Promise<LinkTypeItem[]>
create(projectId: string, item: LinkTypeItem): Promise<LinkTypeItem>
update(projectId: string, item: LinkTypeItem): Promise<LinkTypeItem>
bulkDelete(projectId: string, linkIds: number[]): Promise<void>
}

24
frontend/domain/models/tasks/relation.ts

@ -0,0 +1,24 @@
export class RelationItem {
constructor(
public id: number,
public fromId: number,
public toId: number,
public type: number,
) {
}
static valueOf(
{id, from_id, to_id, type}: { id: number, from_id: number, to_id: number, type: number }
): RelationItem {
return new RelationItem(id, from_id, to_id, type)
}
toObject(): Object {
return {
id: this.id,
from_id: this.fromId,
to_id: this.toId,
type: this.type,
}
}
}

13
frontend/domain/models/tasks/relationRepository.ts

@ -0,0 +1,13 @@
import { RelationItem } from '~/domain/models/tasks/relation'
export interface RelationRepository {
list(projectId: string, exampleId: number): Promise<RelationItem[]>
create(projectId: string, exampleId: number, relation: RelationItem): Promise<RelationItem>
update(projectId: string, exampleId: number, relationId: number, relationType: number): Promise<RelationItem>
delete(projectId: string, exampleId: number, relationId: number): Promise<void>
bulkDelete(projectId: string, exampleId: number, relationIds: number[]): Promise<void>
}

162
frontend/pages/projects/_id/links/index.vue

@ -1,162 +0,0 @@
<template>
<v-card>
<v-card-title>
<action-menu
@create="dialogCreate=true"
@upload="dialogUpload=true"
/>
<v-btn
class="text-capitalize ms-2"
:disabled="!canDelete"
outlined
@click.stop="dialogDelete=true"
>
{{ $t('generic.delete') }}
</v-btn>
<v-dialog v-model="dialogCreate">
<form-create
v-bind.sync="editedItem"
:used-names="usedNames"
@cancel="close"
@save="save"
/>
</v-dialog>
<v-dialog v-model="dialogDelete">
<form-delete
:selected="selected"
@cancel="dialogDelete=false"
@remove="remove"
/>
</v-dialog>
</v-card-title>
<links-list
v-model="selected"
:items="items"
:is-loading="isLoading"
@edit="editItem"
/>
</v-card>
</template>
<script lang="ts">
import Vue from 'vue'
import ActionMenu from '@/components/links/ActionMenu.vue'
import FormCreate from '@/components/links/FormCreate.vue'
import FormDelete from '@/components/links/FormDelete.vue'
import LinksList from '~/components/links/LinksList.vue'
import { LinkTypeDTO } from '~/services/application/links/linkData'
import { ProjectDTO } from '~/services/application/project/projectData'
export default Vue.extend({
components: {
ActionMenu,
FormCreate,
FormDelete,
LinksList
},
layout: 'project',
validate({ params, app }) {
if (/^\d+$/.test(params.id)) {
return app.$services.project.findById(params.id)
.then((res:ProjectDTO) => {
return res.canDefineRelation
})
}
return false
},
data() {
return {
dialogCreate: false,
dialogDelete: false,
dialogUpload: false,
editedIndex: -1,
editedItem: {
text: '',
color: '#ffffff'
} as LinkTypeDTO,
defaultItem: {
text: '',
color: '#ffffff'
} as LinkTypeDTO,
items: [] as LinkTypeDTO[],
selected: [] as LinkTypeDTO[],
isLoading: false,
errorMessage: ''
}
},
async fetch() {
this.isLoading = true
this.items = await this.$services.linkTypes.list(this.projectId)
this.isLoading = false
},
computed: {
canDelete(): boolean {
return this.selected.length > 0
},
projectId(): string {
return this.$route.params.id
},
usedNames(): string[] {
const item = this.items[this.editedIndex] // to remove myself
return this.items.filter(_ => _ !== item).map(item => item.text)
}
},
methods: {
async create() {
await this.$services.linkTypes.create(this.projectId, this.editedItem)
},
async update() {
await this.$services.linkTypes.update(this.projectId, this.editedItem)
},
save() {
if (this.editedIndex > -1) {
this.update()
} else {
this.create()
}
this.$fetch()
this.close()
},
close() {
this.dialogCreate = false
this.$nextTick(() => {
this.editedItem = Object.assign({}, this.defaultItem)
this.editedIndex = -1
})
},
async remove() {
await this.$services.linkTypes.bulkDelete(this.projectId, this.selected)
this.$fetch()
this.dialogDelete = false
this.selected = []
},
clearErrorMessage() {
this.errorMessage = ''
},
editItem(item: LinkTypeDTO) {
this.editedIndex = this.items.indexOf(item)
this.editedItem = Object.assign({}, item)
this.dialogCreate = true
}
}
})
</script>
<style scoped>
::v-deep .v-dialog {
width: 800px;
}
</style>

10
frontend/pages/projects/_id/sequence-labeling/index.vue

@ -43,7 +43,6 @@
</template>
<template #sidebar>
<annotation-progress :progress="progress" />
<list-metadata :metadata="doc.meta" class="mt-4" />
<v-card class="mt-4">
<v-card-title>Label Types</v-card-title>
<v-card-text>
@ -78,6 +77,7 @@
</v-chip-group>
</v-card-text>
</v-card>
<list-metadata :metadata="doc.meta" class="mt-4" />
</template>
</layout-text>
</template>
@ -210,7 +210,7 @@ export default {
async list(docId) {
const annotations = await this.$services.sequenceLabeling.list(this.projectId, docId)
const relations = await this.$services.sequenceLabeling.listLinks(this.projectId, docId)
const relations = await this.$services.sequenceLabeling.listRelations(this.projectId, docId)
// In colab mode, if someone add a new label and annotate data with the label during your work,
// it occurs exception because there is no corresponding label.
await this.maybeFetchEntityTypes(annotations)
@ -234,17 +234,17 @@ export default {
},
async addRelation(fromId, toId, typeId) {
await this.$services.sequenceLabeling.createLink(this.projectId, this.doc.id, fromId, toId, typeId)
await this.$services.sequenceLabeling.createRelation(this.projectId, this.doc.id, fromId, toId, typeId)
await this.list(this.doc.id)
},
async updateRelation(relationId, typeId) {
await this.$services.sequenceLabeling.updateLink(this.projectId, this.doc.id, relationId, typeId)
await this.$services.sequenceLabeling.updateRelation(this.projectId, this.doc.id, relationId, typeId)
await this.list(this.doc.id)
},
async deleteRelation(relationId) {
await this.$services.sequenceLabeling.deleteLink(this.projectId, this.doc.id, relationId)
await this.$services.sequenceLabeling.deleteRelation(this.projectId, this.doc.id, relationId)
await this.list(this.doc.id)
},

4
frontend/plugins/services.ts

@ -41,7 +41,7 @@ import { DownloadApplicationService } from '~/services/application/download/down
import { DownloadFormatApplicationService } from '~/services/application/download/downloadFormatApplicationService'
import { APITagRepository } from '~/repositories/tag/apiTagRepository'
import { TagApplicationService } from '~/services/application/tag/tagApplicationService'
import {ApiLinkRepository} from "~/repositories/links/apiLinkRepository";
import { ApiRelationRepository } from "~/repositories/tasks/sequenceLabeling/apiRelationRepository"
export interface Services {
categoryType: LabelApplicationService,
@ -85,7 +85,7 @@ const plugin: Plugin = (context, inject) => {
const exampleRepository = new APIExampleRepository()
const textClassificationRepository = new APITextClassificationRepository()
const sequenceLabelingRepository = new APISequenceLabelingRepository()
const linkRepository = new ApiLinkRepository()
const linkRepository = new ApiRelationRepository()
const seq2seqRepository = new APISeq2seqRepository()
const optionRepository = new LocalStorageOptionRepository()
const configRepository = new APIConfigRepository()

38
frontend/repositories/links/apiLinkRepository.ts

@ -1,38 +0,0 @@
import ApiService from '@/services/api.service'
import {LinkRepository} from "~/domain/models/links/linkRepository";
import {LinkItem} from "~/domain/models/links/link";
export class ApiLinkRepository implements LinkRepository {
constructor(
private readonly request = ApiService
) {
}
async list(projectId: string, exampleId: number): Promise<LinkItem[]> {
const url = `/projects/${projectId}/examples/${exampleId}/relations`
const response = await this.request.get(url)
return response.data.map((link: any) => LinkItem.valueOf(link))
}
async create(projectId: string, exampleId: number, item: LinkItem): Promise<LinkItem> {
const url = `/projects/${projectId}/examples/${exampleId}/relations`
const response = await this.request.post(url, item.toObject())
return LinkItem.valueOf(response.data)
}
async update(projectId: string, exampleId: number, linkId: number, linkType: number): Promise<LinkItem> {
const url = `/projects/${projectId}/examples/${exampleId}/relations/${linkId}`
const response = await this.request.patch(url, {type: linkType})
return LinkItem.valueOf(response.data)
}
async delete(projectId: string, exampleId: number, linkId: number): Promise<void> {
const url = `/projects/${projectId}/examples/${exampleId}/relations/${linkId}`
const response = await this.request.delete(url)
}
async bulkDelete(projectId: string, exampleId: number, linkIds: number[]): Promise<void> {
const url = `/projects/${projectId}/examples/${exampleId}/relations`
await this.request.delete(url, {ids: linkIds})
}
}

41
frontend/repositories/links/apiLinkTypesRepository.ts

@ -1,41 +0,0 @@
import ApiService from '@/services/api.service'
import { LinkTypesRepository } from "~/domain/models/links/linkTypesRepository";
import { LinkTypeItem } from "~/domain/models/links/link";
export interface LinkTypeResponse {
id: number,
name: string,
color: string
}
export class ApiLinkTypesRepository implements LinkTypesRepository {
constructor(
private readonly request = ApiService
) {}
async list(projectId: string): Promise<LinkTypeItem[]> {
const url = `/projects/${projectId}/relation_types`
const response = await this.request.get(url)
const responseItems: LinkTypeResponse[] = response.data
return responseItems.map(item => LinkTypeItem.valueOf(item))
}
async create(projectId: string, item: LinkTypeItem): Promise<LinkTypeItem> {
const url = `/projects/${projectId}/relation_types`
const response = await this.request.post(url, item.toObject())
const responseItem: LinkTypeResponse = response.data
return LinkTypeItem.valueOf(responseItem)
}
async update(projectId: string, item: LinkTypeItem): Promise<LinkTypeItem> {
const url = `/projects/${projectId}/relation_types/${item.id}`
const response = await this.request.patch(url, item.toObject())
const responseItem: LinkTypeResponse = response.data
return LinkTypeItem.valueOf(responseItem)
}
async bulkDelete(projectId: string, linkIds: number[]): Promise<void> {
const url = `/projects/${projectId}/relation_types`
await this.request.delete(url, { ids: linkIds })
}
}

38
frontend/repositories/tasks/sequenceLabeling/apiRelationRepository.ts

@ -0,0 +1,38 @@
import ApiService from '@/services/api.service'
import { RelationRepository } from "~/domain/models/tasks/relationRepository"
import { RelationItem } from "~/domain/models/tasks/relation"
export class ApiRelationRepository implements RelationRepository {
constructor(
private readonly request = ApiService
) {
}
async list(projectId: string, exampleId: number): Promise<RelationItem[]> {
const url = `/projects/${projectId}/examples/${exampleId}/relations`
const response = await this.request.get(url)
return response.data.map((relation: any) => RelationItem.valueOf(relation))
}
async create(projectId: string, exampleId: number, item: RelationItem): Promise<RelationItem> {
const url = `/projects/${projectId}/examples/${exampleId}/relations`
const response = await this.request.post(url, item.toObject())
return RelationItem.valueOf(response.data)
}
async update(projectId: string, exampleId: number, relationId: number, relationType: number): Promise<RelationItem> {
const url = `/projects/${projectId}/examples/${exampleId}/relations/${relationId}`
const response = await this.request.patch(url, {type: relationType})
return RelationItem.valueOf(response.data)
}
async delete(projectId: string, exampleId: number, relationId: number): Promise<void> {
const url = `/projects/${projectId}/examples/${exampleId}/relations/${relationId}`
const response = await this.request.delete(url)
}
async bulkDelete(projectId: string, exampleId: number, relationIds: number[]): Promise<void> {
const url = `/projects/${projectId}/examples/${exampleId}/relations`
await this.request.delete(url, {ids: relationIds})
}
}

27
frontend/services/application/links/linkData.ts

@ -1,27 +0,0 @@
import { LinkTypeItem, LinkItem } from '~/domain/models/links/link'
export class LinkTypeDTO {
id: number
text: string
color: string
constructor(item: LinkTypeItem) {
this.id = item.id
this.text = item.name
this.color = item.color
}
}
export class LinkDTO {
id: number
fromId: number
toId: number
labelId: number
constructor(item: LinkItem) {
this.id = item.id
this.fromId = item.fromId
this.toId = item.toId
this.labelId = item.type
}
}

30
frontend/services/application/links/linkTypesApplicationService.ts

@ -1,30 +0,0 @@
import { LinkTypeDTO } from './linkData';
import { LinkTypesRepository } from '~/domain/models/links/linkTypesRepository';
import { LinkTypeItem } from '~/domain/models/links/link';
export class LinkTypesApplicationService {
constructor(
private readonly repository: LinkTypesRepository
) {}
public async list(id: string): Promise<LinkTypeDTO[]> {
const items = await this.repository.list(id)
return items.map(item => new LinkTypeDTO(item))
}
public create(projectId: string, item: LinkTypeDTO): void {
const label = new LinkTypeItem(0, item.text, item.color)
this.repository.create(projectId, label)
}
public update(projectId: string, item: LinkTypeDTO): void {
const label = new LinkTypeItem(item.id, item.text, item.color)
this.repository.update(projectId, label)
}
public bulkDelete(projectId: string, items: LinkTypeDTO[]): Promise<void> {
const ids = items.map(item => item.id)
return this.repository.bulkDelete(projectId, ids)
}
}

15
frontend/services/application/tasks/sequenceLabeling/relationData.ts

@ -0,0 +1,15 @@
import { RelationItem } from '~/domain/models/tasks/relation'
export class RelationDTO {
id: number
fromId: number
toId: number
labelId: number
constructor(item: RelationItem) {
this.id = item.id
this.fromId = item.fromId
this.toId = item.toId
this.labelId = item.type
}
}

36
frontend/services/application/tasks/sequenceLabeling/sequenceLabelingApplicationService.ts

@ -1,15 +1,15 @@
import {AnnotationApplicationService} from '../annotationApplicationService'
import { LinkDTO } from '../../links/linkData'
import {SequenceLabelingDTO} from './sequenceLabelingData'
import {APISequenceLabelingRepository} from '~/repositories/tasks/sequenceLabeling/apiSequenceLabeling'
import {SequenceLabelingLabel} from '~/domain/models/tasks/sequenceLabeling'
import {LinkRepository} from "~/domain/models/links/linkRepository"
import {LinkItem} from "~/domain/models/links/link"
import { AnnotationApplicationService } from '../annotationApplicationService'
import { RelationDTO } from './relationData'
import { SequenceLabelingDTO } from './sequenceLabelingData'
import { APISequenceLabelingRepository } from '~/repositories/tasks/sequenceLabeling/apiSequenceLabeling'
import { SequenceLabelingLabel } from '~/domain/models/tasks/sequenceLabeling'
import { RelationRepository } from "~/domain/models/tasks/relationRepository"
import { RelationItem } from "~/domain/models/tasks/relation"
export class SequenceLabelingApplicationService extends AnnotationApplicationService<SequenceLabelingLabel> {
constructor(
readonly repository: APISequenceLabelingRepository,
readonly linkRepository: LinkRepository
readonly relationRepository: RelationRepository
) {
super(new APISequenceLabelingRepository())
}
@ -36,21 +36,21 @@ export class SequenceLabelingApplicationService extends AnnotationApplicationSer
}
}
public async listLinks(projectId: string, docId: number): Promise<LinkDTO[]> {
const items = await this.linkRepository.list(projectId, docId)
return items.map(item => new LinkDTO(item))
public async listRelations(projectId: string, docId: number): Promise<RelationDTO[]> {
const items = await this.relationRepository.list(projectId, docId)
return items.map(item => new RelationDTO(item))
}
public async createLink(projectId: string, docId: number, fromId: number, toId: number, typeId: number): Promise<void> {
const link = new LinkItem(0, fromId, toId, typeId);
await this.linkRepository.create(projectId, docId, link);
public async createRelation(projectId: string, docId: number, fromId: number, toId: number, typeId: number): Promise<void> {
const relation = new RelationItem(0, fromId, toId, typeId)
await this.relationRepository.create(projectId, docId, relation)
}
public async deleteLink(projectId: string, docId: number, linkId: number): Promise<void> {
await this.linkRepository.delete(projectId, docId, linkId);
public async deleteRelation(projectId: string, docId: number, relationId: number): Promise<void> {
await this.relationRepository.delete(projectId, docId, relationId)
}
public async updateLink(projectId: string, docId: number, linkId: number, linkType: number): Promise<void> {
await this.linkRepository.update(projectId, docId, linkId, linkType);
public async updateRelation(projectId: string, docId: number, relationId: number, typeId: number): Promise<void> {
await this.relationRepository.update(projectId, docId, relationId, typeId)
}
}
Loading…
Cancel
Save