Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
chore(systemtags): Add services for global tags and files-specific tags
Signed-off-by: Christopher Ng <[email protected]>
  • Loading branch information
Pytal committed Nov 16, 2023
commit e1a934d90ccfb464db2aa0c2d0fc8b026e36a680
69 changes: 30 additions & 39 deletions apps/systemtags/src/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

import type { FileStat, ResponseDataDetailed } from 'webdav'
import type { ServerTag, Tag, TagWithId } from '../types.js'

Expand All @@ -30,7 +31,7 @@ import { davClient } from './davClient.js'
import { formatTag, parseIdFromLocation, parseTags } from '../utils'
import { logger } from '../logger.js'

const fetchTagsBody = `<?xml version="1.0"?>
export const fetchTagsBody = `<?xml version="1.0"?>
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
<d:prop>
<oc:id />
Expand Down Expand Up @@ -67,39 +68,10 @@ export const fetchLastUsedTagIds = async (): Promise<number[]> => {
}
}

export const fetchSelectedTags = async (fileId: number): Promise<TagWithId[]> => {
const path = '/systemtags-relations/files/' + fileId
try {
const { data: tags } = await davClient.getDirectoryContents(path, {
data: fetchTagsBody,
details: true,
glob: '/systemtags-relations/files/*/*', // Filter out first empty tag
}) as ResponseDataDetailed<Required<FileStat>[]>
return parseTags(tags)
} catch (error) {
logger.error(t('systemtags', 'Failed to load selected tags'), { error })
throw new Error(t('systemtags', 'Failed to load selected tags'))
}
}

export const selectTag = async (fileId: number, tag: Tag | ServerTag): Promise<void> => {
const path = '/systemtags-relations/files/' + fileId + '/' + tag.id
const tagToPut = formatTag(tag)
try {
await davClient.customRequest(path, {
method: 'PUT',
data: tagToPut,
})
} catch (error) {
logger.error(t('systemtags', 'Failed to select tag'), { error })
throw new Error(t('systemtags', 'Failed to select tag'))
}
}

/**
* @return created tag id
*/
export const createTag = async (fileId: number, tag: Tag): Promise<number> => {
export const createTag = async (tag: Tag | ServerTag): Promise<number> => {
const path = '/systemtags'
const tagToPost = formatTag(tag)
try {
Expand All @@ -109,12 +81,7 @@ export const createTag = async (fileId: number, tag: Tag): Promise<number> => {
})
const contentLocation = headers.get('content-location')
if (contentLocation) {
const tagToPut = {
...tagToPost,
id: parseIdFromLocation(contentLocation),
}
await selectTag(fileId, tagToPut)
return tagToPut.id
return parseIdFromLocation(contentLocation)
}
logger.error(t('systemtags', 'Missing "Content-Location" header'))
throw new Error(t('systemtags', 'Missing "Content-Location" header'))
Expand All @@ -124,8 +91,32 @@ export const createTag = async (fileId: number, tag: Tag): Promise<number> => {
}
}

export const deleteTag = async (fileId: number, tag: Tag): Promise<void> => {
const path = '/systemtags-relations/files/' + fileId + '/' + tag.id
export const updateTag = async (tag: TagWithId): Promise<void> => {
const path = '/systemtags/' + tag.id
const data = `<?xml version="1.0"?>
<d:propertyupdate xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
<d:set>
<d:prop>
<oc:display-name>${tag.displayName}</oc:display-name>
<oc:user-visible>${tag.userVisible}</oc:user-visible>
<oc:user-assignable>${tag.userAssignable}</oc:user-assignable>
</d:prop>
</d:set>
</d:propertyupdate>`

try {
await davClient.customRequest(path, {
method: 'PROPPATCH',
data,
})
} catch (error) {
logger.error(t('systemtags', 'Failed to update tag'), { error })
throw new Error(t('systemtags', 'Failed to update tag'))
}
}

export const deleteTag = async (tag: TagWithId): Promise<void> => {
const path = '/systemtags/' + tag.id
try {
await davClient.deleteFile(path)
} catch (error) {
Expand Down
82 changes: 82 additions & 0 deletions apps/systemtags/src/services/files.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* @copyright 2023 Christopher Ng <[email protected]>
*
* @author Christopher Ng <[email protected]>
*
* @license AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

import type { FileStat, ResponseDataDetailed } from 'webdav'
import type { ServerTagWithId, Tag, TagWithId } from '../types.js'

import { davClient } from './davClient.js'
import { createTag, fetchTagsBody } from './api.js'
import { formatTag, parseTags } from '../utils.js'
import { logger } from '../logger.js'

export const fetchTagsForFile = async (fileId: number): Promise<TagWithId[]> => {
const path = '/systemtags-relations/files/' + fileId
try {
const { data: tags } = await davClient.getDirectoryContents(path, {
data: fetchTagsBody,
details: true,
glob: '/systemtags-relations/files/*/*', // Filter out first empty tag
}) as ResponseDataDetailed<Required<FileStat>[]>
return parseTags(tags)
} catch (error) {
logger.error(t('systemtags', 'Failed to load tags for file'), { error })
throw new Error(t('systemtags', 'Failed to load tags for file'))
}
}

/**
* @return created tag id
*/
export const createTagForFile = async (tag: Tag, fileId: number): Promise<number> => {
const tagToCreate = formatTag(tag)
const tagId = await createTag(tagToCreate)
const tagToSet: ServerTagWithId = {
...tagToCreate,
id: tagId,
}
await setTagForFile(tagToSet, fileId)
return tagToSet.id
}

export const setTagForFile = async (tag: TagWithId | ServerTagWithId, fileId: number): Promise<void> => {
const path = '/systemtags-relations/files/' + fileId + '/' + tag.id
const tagToPut = formatTag(tag)
try {
await davClient.customRequest(path, {
method: 'PUT',
data: tagToPut,
})
} catch (error) {
logger.error(t('systemtags', 'Failed to set tag for file'), { error })
throw new Error(t('systemtags', 'Failed to set tag for file'))
}
}

export const deleteTagForFile = async (tag: TagWithId, fileId: number): Promise<void> => {
const path = '/systemtags-relations/files/' + fileId + '/' + tag.id
try {
await davClient.deleteFile(path)
} catch (error) {
logger.error(t('systemtags', 'Failed to delete tag for file'), { error })
throw new Error(t('systemtags', 'Failed to delete tag for file'))
}
}
2 changes: 2 additions & 0 deletions apps/systemtags/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@ export type TagWithId = Required<Tag>
export type ServerTag = BaseTag & {
name: string
}

export type ServerTagWithId = Required<ServerTag>