2020-10-20 12:50:53 -07:00
|
|
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
|
|
// See LICENSE.txt for license information.
|
2022-06-13 13:35:42 +05:30
|
|
|
import {Block, BlockPatch, FileInfo} from './blocks/block'
|
2022-03-22 15:24:34 +01:00
|
|
|
import {Board, BoardsAndBlocks, BoardsAndBlocksPatch, BoardPatch, BoardMember} from './blocks/board'
|
2021-01-12 16:52:25 -08:00
|
|
|
import {ISharing} from './blocks/sharing'
|
2021-08-12 02:39:02 -07:00
|
|
|
import {OctoUtils} from './octoUtils'
|
2022-08-29 12:55:12 +05:30
|
|
|
import {IUser, UserConfigPatch, UserPreference} from './user'
|
2020-10-20 12:52:56 -07:00
|
|
|
import {Utils} from './utils'
|
2021-09-01 15:53:27 -06:00
|
|
|
import {ClientConfig} from './config/clientConfig'
|
2021-09-15 12:18:25 +05:30
|
|
|
import {UserSettings} from './userSettings'
|
2022-04-14 00:10:53 +02:00
|
|
|
import {Category, CategoryBoards} from './store/sidebar'
|
2022-07-07 16:46:53 +02:00
|
|
|
import {Channel} from './store/channels'
|
2022-03-22 15:24:34 +01:00
|
|
|
import {Team} from './store/teams'
|
2021-12-10 10:46:37 -05:00
|
|
|
import {Subscription} from './wsclient'
|
2022-02-28 16:58:16 +05:30
|
|
|
import {PrepareOnboardingResponse} from './onboardingTour'
|
2022-09-14 15:11:50 +02:00
|
|
|
import {Constants} from './constants'
|
2022-06-29 18:05:24 +05:30
|
|
|
|
2022-06-20 20:39:20 +02:00
|
|
|
import {BoardsCloudLimits} from './boardsCloudLimits'
|
[MM-43781] Feature: boards insights (#3005)
* Add boilerplate functions and handlers for boards insights
* Fix function signatures to add 'duration' parameter, fix where clauses in db queries
* Fix where clause to include boards of which userId in parameter is a member
* Modify queries to work with sqlite, postgres, mysql
* Integration tests, and results of make generate
* Lint Fixes
* Add icons to board insights
* Lint fixes
* Format insights queries without squirrel to fix parameterization issues
* Add tests for sqlstore utility functions
* Improve team insights tests by creating 2 boards
* Refactor endpoints/app to adhere to developments in 7.0 release
* Refactor queries to use squirrel
* Lint fixes
* Fix client, integration tests
* Remove old integration tests
* Add storetests, refactor functions to handle authorized board_ids
* Make queries compatible with mysql, sqlite
* Add app tests
* Fix lint errors
* Revert makefile changes, fix docstring in api
* Lint fixes and doc correction suggested by @wiggin77
* Fix mock store call count error
* adding client code
* Make the following changes
- use serviceAPI to get user.Timezone
- rename licenseAndGuestUserCheck to insightPermissionGate, and handle returned error better
- validate page, perPage parameters aren't < 0
* Lint fix
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Co-authored-by: Benjamin Cooke <benjamincooke@Benjamins-MacBook-Pro.local>
2022-08-08 11:42:02 +05:30
|
|
|
import {TopBoardResponse} from './insights'
|
2022-10-25 14:28:00 -06:00
|
|
|
import {BoardSiteStatistics} from './statistics'
|
2020-10-08 09:21:27 -07:00
|
|
|
|
|
|
|
//
|
|
|
|
// OctoClient is the client interface to the server APIs
|
|
|
|
//
|
|
|
|
class OctoClient {
|
2021-09-07 10:08:02 +05:30
|
|
|
readonly serverUrl: string | undefined
|
|
|
|
private logged = false
|
|
|
|
|
|
|
|
// this need to be a function rather than a const because
|
|
|
|
// one of the global variable (`window.baseURL`) is set at runtime
|
|
|
|
// after the first instance of OctoClient is created.
|
|
|
|
// Avoiding the race condition becomes more complex than making
|
|
|
|
// the base URL dynamic though a function
|
|
|
|
private getBaseURL(): string {
|
|
|
|
const baseURL = (this.serverUrl || Utils.getBaseURL(true)).replace(/\/$/, '')
|
|
|
|
|
|
|
|
// Logging this for debugging.
|
|
|
|
// Logging just once to avoid log noise.
|
|
|
|
if (!this.logged) {
|
|
|
|
Utils.log(`OctoClient baseURL: ${baseURL}`)
|
|
|
|
this.logged = true
|
|
|
|
}
|
|
|
|
|
|
|
|
return baseURL
|
|
|
|
}
|
|
|
|
|
2021-02-09 16:34:54 -08:00
|
|
|
get token(): string {
|
2021-04-16 13:42:37 +02:00
|
|
|
return localStorage.getItem('focalboardSessionId') || ''
|
2021-02-09 16:34:54 -08:00
|
|
|
}
|
2021-03-26 11:01:54 -07:00
|
|
|
set token(value: string) {
|
2021-04-16 13:42:37 +02:00
|
|
|
localStorage.setItem('focalboardSessionId', value)
|
2021-03-26 11:01:54 -07:00
|
|
|
}
|
2021-03-29 16:27:35 -07:00
|
|
|
|
2022-03-31 17:55:29 +05:30
|
|
|
constructor(serverUrl?: string, public teamId = Constants.globalTeamId) {
|
2021-09-07 10:08:02 +05:30
|
|
|
this.serverUrl = serverUrl
|
2020-10-20 12:50:53 -07:00
|
|
|
}
|
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
private async getJson<T>(response: Response, defaultValue: T): Promise<T> {
|
2021-01-14 10:58:16 -08:00
|
|
|
// The server may return null or malformed json
|
|
|
|
try {
|
2021-03-08 16:08:17 -08:00
|
|
|
const value = await response.json()
|
|
|
|
return value || defaultValue
|
2021-01-14 10:58:16 -08:00
|
|
|
} catch {
|
|
|
|
return defaultValue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-04 16:03:09 +01:00
|
|
|
async login(username: string, password: string): Promise<boolean> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = '/api/v2/login'
|
2020-12-04 16:03:09 +01:00
|
|
|
const body = JSON.stringify({username, password, type: 'normal'})
|
2021-09-07 10:08:02 +05:30
|
|
|
const response = await fetch(this.getBaseURL() + path, {
|
2020-12-04 16:03:09 +01:00
|
|
|
method: 'POST',
|
|
|
|
headers: this.headers(),
|
|
|
|
body,
|
|
|
|
})
|
2021-01-14 09:34:08 -08:00
|
|
|
if (response.status !== 200) {
|
2020-12-04 16:03:09 +01:00
|
|
|
return false
|
|
|
|
}
|
2021-01-14 09:34:08 -08:00
|
|
|
|
2021-03-17 12:15:16 -07:00
|
|
|
const responseJson = (await this.getJson(response, {})) as {token?: string}
|
2021-02-10 14:09:06 -08:00
|
|
|
if (responseJson.token) {
|
2021-04-16 13:42:37 +02:00
|
|
|
localStorage.setItem('focalboardSessionId', responseJson.token)
|
2021-01-14 09:34:08 -08:00
|
|
|
return true
|
|
|
|
}
|
2020-12-04 16:03:09 +01:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-12-01 10:21:31 +01:00
|
|
|
async logout(): Promise<boolean> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = '/api/v2/logout'
|
2021-12-01 10:21:31 +01:00
|
|
|
const response = await fetch(this.getBaseURL() + path, {
|
|
|
|
method: 'POST',
|
|
|
|
headers: this.headers(),
|
|
|
|
})
|
2021-04-16 13:42:37 +02:00
|
|
|
localStorage.removeItem('focalboardSessionId')
|
2021-12-01 10:21:31 +01:00
|
|
|
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
2021-01-14 18:23:15 -08:00
|
|
|
}
|
|
|
|
|
2021-09-01 15:53:27 -06:00
|
|
|
async getClientConfig(): Promise<ClientConfig | null> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = '/api/v2/clientConfig'
|
2021-09-09 15:35:30 +05:30
|
|
|
const response = await fetch(this.getBaseURL() + path, {
|
2021-09-01 15:53:27 -06:00
|
|
|
method: 'GET',
|
|
|
|
headers: this.headers(),
|
|
|
|
})
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
const json = (await this.getJson(response, {})) as ClientConfig
|
|
|
|
return json
|
|
|
|
}
|
|
|
|
|
2021-10-04 06:44:30 -03:00
|
|
|
async register(email: string, username: string, password: string, token?: string): Promise<{code: number, json: {error?: string}}> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = '/api/v2/register'
|
2021-01-13 16:56:01 -08:00
|
|
|
const body = JSON.stringify({email, username, password, token})
|
2021-09-07 10:08:02 +05:30
|
|
|
const response = await fetch(this.getBaseURL() + path, {
|
2020-12-04 16:03:09 +01:00
|
|
|
method: 'POST',
|
|
|
|
headers: this.headers(),
|
|
|
|
body,
|
2021-01-21 10:16:40 -08:00
|
|
|
})
|
2021-10-04 06:44:30 -03:00
|
|
|
const json = (await this.getJson(response, {})) as {error?: string}
|
2021-01-21 10:16:40 -08:00
|
|
|
return {code: response.status, json}
|
|
|
|
}
|
|
|
|
|
2021-10-04 06:44:30 -03:00
|
|
|
async changePassword(userId: string, oldPassword: string, newPassword: string): Promise<{code: number, json: {error?: string}}> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = `/api/v2/users/${encodeURIComponent(userId)}/changepassword`
|
2021-01-21 10:16:40 -08:00
|
|
|
const body = JSON.stringify({oldPassword, newPassword})
|
2021-09-07 10:08:02 +05:30
|
|
|
const response = await fetch(this.getBaseURL() + path, {
|
2021-01-21 10:16:40 -08:00
|
|
|
method: 'POST',
|
|
|
|
headers: this.headers(),
|
|
|
|
body,
|
2020-12-04 16:03:09 +01:00
|
|
|
})
|
2021-10-04 06:44:30 -03:00
|
|
|
const json = (await this.getJson(response, {})) as {error?: string}
|
2021-01-14 10:58:16 -08:00
|
|
|
return {code: response.status, json}
|
2020-12-04 16:03:09 +01:00
|
|
|
}
|
|
|
|
|
2021-01-11 15:16:39 +01:00
|
|
|
private headers() {
|
2020-12-04 16:03:09 +01:00
|
|
|
return {
|
|
|
|
Accept: 'application/json',
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
Authorization: this.token ? 'Bearer ' + this.token : '',
|
2021-02-02 16:54:15 -08:00
|
|
|
'X-Requested-With': 'XMLHttpRequest',
|
2020-12-04 16:03:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
private teamPath(teamId?: string): string {
|
|
|
|
let teamIdToUse = teamId
|
|
|
|
if (!teamId) {
|
2022-03-31 17:55:29 +05:30
|
|
|
teamIdToUse = this.teamId === Constants.globalTeamId ? UserSettings.lastTeamId || this.teamId : this.teamId
|
2021-09-20 20:06:05 +05:30
|
|
|
}
|
|
|
|
|
2022-04-13 22:24:32 +02:00
|
|
|
return `/api/v2/teams/${teamIdToUse}`
|
2022-03-22 15:24:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private teamsPath(): string {
|
2022-04-13 22:24:32 +02:00
|
|
|
return '/api/v2/teams'
|
2021-03-26 11:01:54 -07:00
|
|
|
}
|
|
|
|
|
2021-01-14 09:34:08 -08:00
|
|
|
async getMe(): Promise<IUser | undefined> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = '/api/v2/users/me'
|
2021-09-07 10:08:02 +05:30
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
2021-01-14 09:34:08 -08:00
|
|
|
if (response.status !== 200) {
|
|
|
|
return undefined
|
|
|
|
}
|
2021-03-17 12:15:16 -07:00
|
|
|
const user = (await this.getJson(response, {})) as IUser
|
2020-12-07 20:40:16 +01:00
|
|
|
return user
|
|
|
|
}
|
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
async getMyBoardMemberships(): Promise<BoardMember[]> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = '/api/v2/users/me/memberships'
|
2022-03-22 15:24:34 +01:00
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
const members = (await this.getJson(response, [])) as BoardMember[]
|
|
|
|
return members
|
|
|
|
}
|
|
|
|
|
2021-01-19 15:12:54 -08:00
|
|
|
async getUser(userId: string): Promise<IUser | undefined> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = `/api/v2/users/${encodeURIComponent(userId)}`
|
2021-09-07 10:08:02 +05:30
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
2021-01-19 15:12:54 -08:00
|
|
|
if (response.status !== 200) {
|
|
|
|
return undefined
|
|
|
|
}
|
2021-03-17 12:15:16 -07:00
|
|
|
const user = (await this.getJson(response, {})) as IUser
|
2021-01-19 15:12:54 -08:00
|
|
|
return user
|
|
|
|
}
|
|
|
|
|
2022-07-30 01:58:00 +05:30
|
|
|
async getUsersList(userIds: string[]): Promise<IUser[] | []> {
|
2022-09-14 15:11:50 +02:00
|
|
|
const path = '/api/v2/users'
|
2022-07-30 01:58:00 +05:30
|
|
|
const body = JSON.stringify(userIds)
|
|
|
|
const response = await fetch(this.getBaseURL() + path, {
|
|
|
|
headers: this.headers(),
|
|
|
|
method: 'POST',
|
|
|
|
body,
|
|
|
|
})
|
|
|
|
|
2022-09-14 15:11:50 +02:00
|
|
|
if (response.status !== 200) {
|
2022-07-30 01:58:00 +05:30
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
|
|
|
return (await this.getJson(response, [])) as IUser[]
|
|
|
|
}
|
|
|
|
|
2022-09-14 15:11:50 +02:00
|
|
|
async getMyConfig(): Promise<UserPreference[] | undefined> {
|
|
|
|
const path = '/api/v2/users/me/config'
|
2022-08-29 12:55:12 +05:30
|
|
|
const response = await fetch(this.getBaseURL() + path, {
|
|
|
|
headers: this.headers(),
|
2022-09-14 15:11:50 +02:00
|
|
|
method: 'GET',
|
2022-08-29 12:55:12 +05:30
|
|
|
})
|
|
|
|
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
2022-09-14 15:11:50 +02:00
|
|
|
return (await this.getJson(response, [])) as UserPreference[]
|
2022-08-29 12:55:12 +05:30
|
|
|
}
|
|
|
|
|
2022-09-14 15:11:50 +02:00
|
|
|
async patchUserConfig(userID: string, patch: UserConfigPatch): Promise<UserPreference[] | undefined> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = `/api/v2/users/${encodeURIComponent(userID)}/config`
|
2022-02-28 16:58:16 +05:30
|
|
|
const body = JSON.stringify(patch)
|
|
|
|
const response = await fetch(this.getBaseURL() + path, {
|
|
|
|
headers: this.headers(),
|
|
|
|
method: 'PUT',
|
|
|
|
body,
|
|
|
|
})
|
|
|
|
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
2022-09-14 15:11:50 +02:00
|
|
|
return (await this.getJson(response, {})) as UserPreference[]
|
2022-02-28 16:58:16 +05:30
|
|
|
}
|
|
|
|
|
2022-04-13 14:21:25 +02:00
|
|
|
async exportBoardArchive(boardID: string): Promise<Response> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = `/api/v2/boards/${boardID}/archive/export`
|
2022-02-01 19:01:29 -05:00
|
|
|
return fetch(this.getBaseURL() + path, {headers: this.headers()})
|
2022-04-13 14:21:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
async exportFullArchive(teamID: string): Promise<Response> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = `/api/v2/teams/${teamID}/archive/export`
|
2022-04-13 14:21:25 +02:00
|
|
|
return fetch(this.getBaseURL() + path, {headers: this.headers()})
|
2020-10-20 12:50:53 -07:00
|
|
|
}
|
|
|
|
|
2022-02-01 19:01:29 -05:00
|
|
|
async importFullArchive(file: File): Promise<Response> {
|
|
|
|
const formData = new FormData()
|
|
|
|
formData.append('file', file)
|
|
|
|
|
|
|
|
const headers = this.headers() as Record<string, string>
|
|
|
|
|
|
|
|
// TIPTIP: Leave out Content-Type here, it will be automatically set by the browser
|
|
|
|
delete headers['Content-Type']
|
2021-03-02 15:35:44 -08:00
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
return fetch(this.getBaseURL() + this.teamPath() + '/archive/import', {
|
2020-10-21 15:03:12 -07:00
|
|
|
method: 'POST',
|
2022-02-01 19:01:29 -05:00
|
|
|
headers,
|
|
|
|
body: formData,
|
2020-10-21 15:03:12 -07:00
|
|
|
})
|
2020-10-20 12:50:53 -07:00
|
|
|
}
|
|
|
|
|
2021-08-02 17:46:00 +02:00
|
|
|
async getBlocksWithParent(parentId: string, type?: string): Promise<Block[]> {
|
2020-10-20 12:50:53 -07:00
|
|
|
let path: string
|
2020-10-21 13:20:00 -07:00
|
|
|
if (type) {
|
2022-03-22 15:24:34 +01:00
|
|
|
path = this.teamPath() + `/blocks?parent_id=${encodeURIComponent(parentId)}&type=${encodeURIComponent(type)}`
|
2020-10-20 12:50:53 -07:00
|
|
|
} else {
|
2022-03-22 15:24:34 +01:00
|
|
|
path = this.teamPath() + `/blocks?parent_id=${encodeURIComponent(parentId)}`
|
2020-10-20 12:50:53 -07:00
|
|
|
}
|
2020-10-21 13:20:00 -07:00
|
|
|
return this.getBlocksWithPath(path)
|
|
|
|
}
|
|
|
|
|
2021-08-02 17:46:00 +02:00
|
|
|
async getBlocksWithType(type: string): Promise<Block[]> {
|
2022-03-22 15:24:34 +01:00
|
|
|
const path = this.teamPath() + `/blocks?type=${encodeURIComponent(type)}`
|
2020-10-21 13:20:00 -07:00
|
|
|
return this.getBlocksWithPath(path)
|
|
|
|
}
|
2020-10-20 12:50:53 -07:00
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
async getBlocksWithBlockID(blockID: string, boardID: string, optionalReadToken?: string): Promise<Block[]> {
|
2022-04-13 22:24:32 +02:00
|
|
|
let path = `/api/v2/boards/${boardID}/blocks?block_id=${blockID}`
|
2021-10-22 12:13:31 -04:00
|
|
|
const readToken = optionalReadToken || Utils.getReadToken()
|
|
|
|
if (readToken) {
|
|
|
|
path += `&read_token=${readToken}`
|
|
|
|
}
|
|
|
|
return this.getBlocksWithPath(path)
|
|
|
|
}
|
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
async getAllBlocks(boardID: string): Promise<Block[]> {
|
2022-04-13 22:24:32 +02:00
|
|
|
let path = `/api/v2/boards/${boardID}/blocks?all=true`
|
2022-03-25 10:46:58 -06:00
|
|
|
const readToken = Utils.getReadToken()
|
|
|
|
if (readToken) {
|
|
|
|
path += `&read_token=${readToken}`
|
|
|
|
}
|
2021-08-02 17:46:00 +02:00
|
|
|
return this.getBlocksWithPath(path)
|
|
|
|
}
|
|
|
|
|
|
|
|
private async getBlocksWithPath(path: string): Promise<Block[]> {
|
2021-09-07 10:08:02 +05:30
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
2021-01-12 18:49:08 -08:00
|
|
|
if (response.status !== 200) {
|
|
|
|
return []
|
|
|
|
}
|
2021-08-02 17:46:00 +02:00
|
|
|
const blocks = (await this.getJson(response, [])) as Block[]
|
2021-08-12 02:39:02 -07:00
|
|
|
return this.fixBlocks(blocks)
|
2020-10-20 12:50:53 -07:00
|
|
|
}
|
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
private async getBoardsWithPath(path: string): Promise<Board[]> {
|
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
const boards = (await this.getJson(response, [])) as Board[]
|
|
|
|
return boards
|
|
|
|
}
|
|
|
|
|
|
|
|
private async getBoardMembersWithPath(path: string): Promise<BoardMember[]> {
|
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
const boardMembers = (await this.getJson(response, [])) as BoardMember[]
|
|
|
|
return boardMembers
|
|
|
|
}
|
|
|
|
|
2021-08-12 02:39:02 -07:00
|
|
|
fixBlocks(blocks: Block[]): Block[] {
|
2020-10-21 15:03:12 -07:00
|
|
|
if (!blocks) {
|
2021-08-12 02:39:02 -07:00
|
|
|
return []
|
2020-10-20 12:50:53 -07:00
|
|
|
}
|
|
|
|
|
2021-08-12 02:39:02 -07:00
|
|
|
// Hydrate is important, as it ensures that each block is complete to the current model
|
|
|
|
const fixedBlocks = OctoUtils.hydrateBlocks(blocks)
|
|
|
|
|
|
|
|
return fixedBlocks
|
2020-10-20 12:50:53 -07:00
|
|
|
}
|
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
async patchBlock(boardId: string, blockId: string, blockPatch: BlockPatch): Promise<Response> {
|
2021-11-05 11:54:27 +01:00
|
|
|
Utils.log(`patchBlock: ${blockId} block`)
|
2021-08-06 14:10:24 +02:00
|
|
|
const body = JSON.stringify(blockPatch)
|
2022-04-13 22:24:32 +02:00
|
|
|
return fetch(`${this.getBaseURL()}/api/v2/boards/${boardId}/blocks/${blockId}`, {
|
2021-08-06 14:10:24 +02:00
|
|
|
method: 'PATCH',
|
|
|
|
headers: this.headers(),
|
|
|
|
body,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-12-10 07:17:00 -07:00
|
|
|
async patchBlocks(blocks: Block[], blockPatches: BlockPatch[]): Promise<Response> {
|
|
|
|
Utils.log(`patchBlocks: ${blocks.length} blocks`)
|
|
|
|
const blockIds = blocks.map((block) => block.id)
|
|
|
|
const body = JSON.stringify({block_ids: blockIds, block_patches: blockPatches})
|
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
const path = this.getBaseURL() + this.teamPath() + '/blocks'
|
2021-12-10 07:17:00 -07:00
|
|
|
const response = fetch(path, {
|
|
|
|
method: 'PATCH',
|
|
|
|
headers: this.headers(),
|
|
|
|
body,
|
|
|
|
})
|
|
|
|
return response
|
|
|
|
}
|
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
async deleteBlock(boardId: string, blockId: string): Promise<Response> {
|
|
|
|
Utils.log(`deleteBlock: ${blockId} on board ${boardId}`)
|
2022-04-13 22:24:32 +02:00
|
|
|
return fetch(`${this.getBaseURL()}/api/v2/boards/${boardId}/blocks/${encodeURIComponent(blockId)}`, {
|
2020-10-20 12:50:53 -07:00
|
|
|
method: 'DELETE',
|
2020-12-04 16:03:09 +01:00
|
|
|
headers: this.headers(),
|
2020-10-20 12:50:53 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-04-08 19:31:28 +02:00
|
|
|
async undeleteBlock(boardId: string, blockId: string): Promise<Response> {
|
2022-02-22 18:42:49 +01:00
|
|
|
Utils.log(`undeleteBlock: ${blockId}`)
|
2022-04-13 22:24:32 +02:00
|
|
|
return fetch(`${this.getBaseURL()}/api/v2/boards/${encodeURIComponent(boardId)}/blocks/${encodeURIComponent(blockId)}/undelete`, {
|
2022-04-08 19:31:28 +02:00
|
|
|
method: 'POST',
|
|
|
|
headers: this.headers(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
async undeleteBoard(boardId: string): Promise<Response> {
|
|
|
|
Utils.log(`undeleteBoard: ${boardId}`)
|
2022-04-13 22:24:32 +02:00
|
|
|
return fetch(`${this.getBaseURL()}/api/v2/boards/${boardId}/undelete`, {
|
2022-02-22 18:42:49 +01:00
|
|
|
method: 'POST',
|
|
|
|
headers: this.headers(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-12-10 10:46:37 -05:00
|
|
|
async followBlock(blockId: string, blockType: string, userId: string): Promise<Response> {
|
|
|
|
const body: Subscription = {
|
|
|
|
blockType,
|
|
|
|
blockId,
|
|
|
|
subscriberType: 'user',
|
|
|
|
subscriberId: userId,
|
|
|
|
}
|
|
|
|
|
2022-04-13 22:24:32 +02:00
|
|
|
return fetch(this.getBaseURL() + '/api/v2/subscriptions', {
|
2021-12-10 10:46:37 -05:00
|
|
|
method: 'POST',
|
|
|
|
headers: this.headers(),
|
|
|
|
body: JSON.stringify(body),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
async unfollowBlock(blockId: string, blockType: string, userId: string): Promise<Response> {
|
2022-04-13 22:24:32 +02:00
|
|
|
return fetch(this.getBaseURL() + `/api/v2/subscriptions/${blockId}/${userId}`, {
|
2021-12-10 10:46:37 -05:00
|
|
|
method: 'DELETE',
|
|
|
|
headers: this.headers(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
async insertBlock(boardId: string, block: Block): Promise<Response> {
|
|
|
|
return this.insertBlocks(boardId, [block])
|
2020-10-20 12:50:53 -07:00
|
|
|
}
|
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
async insertBlocks(boardId: string, blocks: Block[], sourceBoardID?: string): Promise<Response> {
|
|
|
|
Utils.log(`insertBlocks: ${blocks.length} blocks(s) on board ${boardId}`)
|
2020-10-21 15:03:12 -07:00
|
|
|
blocks.forEach((block) => {
|
2020-11-12 10:16:59 -08:00
|
|
|
Utils.log(`\t ${block.type}, ${block.id}, ${block.title?.substr(0, 50) || ''}`)
|
2020-10-21 15:03:12 -07:00
|
|
|
})
|
|
|
|
const body = JSON.stringify(blocks)
|
2022-04-13 22:24:32 +02:00
|
|
|
return fetch(`${this.getBaseURL()}/api/v2/boards/${boardId}/blocks` + (sourceBoardID ? `?sourceBoardID=${encodeURIComponent(sourceBoardID)}` : ''), {
|
2020-10-20 12:50:53 -07:00
|
|
|
method: 'POST',
|
2020-12-04 16:03:09 +01:00
|
|
|
headers: this.headers(),
|
2020-10-20 12:50:53 -07:00
|
|
|
body,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
async createBoardsAndBlocks(bab: BoardsAndBlocks): Promise<Response> {
|
|
|
|
Utils.log(`createBoardsAndBlocks: ${bab.boards.length} board(s) ${bab.blocks.length} block(s)`)
|
|
|
|
bab.boards.forEach((board: Board) => {
|
|
|
|
Utils.log(`\t Board ${board.id}, ${board.type}, ${board.title?.substr(0, 50) || ''}`)
|
|
|
|
})
|
|
|
|
bab.blocks.forEach((block: Block) => {
|
|
|
|
Utils.log(`\t Block ${block.id}, ${block.type}, ${block.title?.substr(0, 50) || ''}`)
|
|
|
|
})
|
|
|
|
|
|
|
|
const body = JSON.stringify(bab)
|
2022-04-13 22:24:32 +02:00
|
|
|
return fetch(this.getBaseURL() + '/api/v2/boards-and-blocks', {
|
2022-03-22 15:24:34 +01:00
|
|
|
method: 'POST',
|
|
|
|
headers: this.headers(),
|
|
|
|
body,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
async deleteBoardsAndBlocks(boardIds: string[], blockIds: string[]): Promise<Response> {
|
|
|
|
Utils.log(`deleteBoardsAndBlocks: ${boardIds.length} board(s) ${blockIds.length} block(s)`)
|
|
|
|
Utils.log(`\t Boards ${boardIds.join(', ')}`)
|
|
|
|
Utils.log(`\t Blocks ${blockIds.join(', ')}`)
|
|
|
|
|
|
|
|
const body = JSON.stringify({boards: boardIds, blocks: blockIds})
|
2022-04-13 22:24:32 +02:00
|
|
|
return fetch(this.getBaseURL() + '/api/v2/boards-and-blocks', {
|
2022-03-22 15:24:34 +01:00
|
|
|
method: 'DELETE',
|
|
|
|
headers: this.headers(),
|
|
|
|
body,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// BoardMember
|
|
|
|
async createBoardMember(member: Partial<BoardMember>): Promise<BoardMember|undefined> {
|
|
|
|
Utils.log(`createBoardMember: user ${member.userId} and board ${member.boardId}`)
|
|
|
|
|
|
|
|
const body = JSON.stringify(member)
|
2022-04-13 22:24:32 +02:00
|
|
|
const response = await fetch(this.getBaseURL() + `/api/v2/boards/${member.boardId}/members`, {
|
2022-03-22 15:24:34 +01:00
|
|
|
method: 'POST',
|
|
|
|
headers: this.headers(),
|
|
|
|
body,
|
|
|
|
})
|
|
|
|
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.getJson<BoardMember>(response, {} as BoardMember)
|
|
|
|
}
|
|
|
|
|
2022-03-29 01:14:33 -07:00
|
|
|
async joinBoard(boardId: string): Promise<BoardMember|undefined> {
|
|
|
|
Utils.log(`joinBoard: board ${boardId}`)
|
|
|
|
|
2022-04-13 22:24:32 +02:00
|
|
|
const response = await fetch(this.getBaseURL() + `/api/v2/boards/${boardId}/join`, {
|
2022-03-29 01:14:33 -07:00
|
|
|
method: 'POST',
|
2022-09-14 15:11:50 +02:00
|
|
|
headers: this.headers(),
|
2022-03-29 01:14:33 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.getJson<BoardMember>(response, {} as BoardMember)
|
|
|
|
}
|
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
async updateBoardMember(member: BoardMember): Promise<Response> {
|
|
|
|
Utils.log(`udpateBoardMember: user ${member.userId} and board ${member.boardId}`)
|
|
|
|
|
|
|
|
const body = JSON.stringify(member)
|
2022-04-13 22:24:32 +02:00
|
|
|
return fetch(this.getBaseURL() + `/api/v2/boards/${member.boardId}/members/${member.userId}`, {
|
2022-03-22 15:24:34 +01:00
|
|
|
method: 'PUT',
|
|
|
|
headers: this.headers(),
|
|
|
|
body,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
async deleteBoardMember(member: BoardMember): Promise<Response> {
|
|
|
|
Utils.log(`deleteBoardMember: user ${member.userId} and board ${member.boardId}`)
|
2021-01-12 16:52:25 -08:00
|
|
|
|
2022-04-13 22:24:32 +02:00
|
|
|
return fetch(this.getBaseURL() + `/api/v2/boards/${member.boardId}/members/${member.userId}`, {
|
2022-03-22 15:24:34 +01:00
|
|
|
method: 'DELETE',
|
|
|
|
headers: this.headers(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
async patchBoardsAndBlocks(babp: BoardsAndBlocksPatch): Promise<Response> {
|
|
|
|
Utils.log(`patchBoardsAndBlocks: ${babp.boardIDs.length} board(s) ${babp.blockIDs.length} block(s)`)
|
|
|
|
Utils.log(`\t Board ${babp.boardIDs.join(', ')}`)
|
|
|
|
Utils.log(`\t Blocks ${babp.blockIDs.join(', ')}`)
|
|
|
|
|
|
|
|
const body = JSON.stringify(babp)
|
2022-04-13 22:24:32 +02:00
|
|
|
return fetch(this.getBaseURL() + '/api/v2/boards-and-blocks', {
|
2022-03-22 15:24:34 +01:00
|
|
|
method: 'PATCH',
|
|
|
|
headers: this.headers(),
|
|
|
|
body,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sharing
|
|
|
|
async getSharing(boardID: string): Promise<ISharing | undefined> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = `/api/v2/boards/${boardID}/sharing`
|
2021-09-07 10:08:02 +05:30
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
2021-01-14 09:34:08 -08:00
|
|
|
if (response.status !== 200) {
|
|
|
|
return undefined
|
|
|
|
}
|
2022-03-22 15:24:34 +01:00
|
|
|
return this.getJson(response, undefined)
|
2021-01-12 16:52:25 -08:00
|
|
|
}
|
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
async setSharing(boardID: string, sharing: ISharing): Promise<boolean> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = `/api/v2/boards/${boardID}/sharing`
|
2021-01-12 16:52:25 -08:00
|
|
|
const body = JSON.stringify(sharing)
|
|
|
|
const response = await fetch(
|
2021-09-07 10:08:02 +05:30
|
|
|
this.getBaseURL() + path,
|
2021-01-12 16:52:25 -08:00
|
|
|
{
|
|
|
|
method: 'POST',
|
|
|
|
headers: this.headers(),
|
|
|
|
body,
|
|
|
|
},
|
|
|
|
)
|
2021-01-14 09:34:08 -08:00
|
|
|
if (response.status !== 200) {
|
|
|
|
return false
|
2021-01-12 16:52:25 -08:00
|
|
|
}
|
2021-01-14 09:34:08 -08:00
|
|
|
|
|
|
|
return true
|
2021-01-12 16:52:25 -08:00
|
|
|
}
|
2021-01-13 16:56:01 -08:00
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
async regenerateTeamSignupToken(): Promise<void> {
|
|
|
|
const path = this.teamPath() + '/regenerate_signup_token'
|
|
|
|
await fetch(this.getBaseURL() + path, {
|
2021-01-13 16:56:01 -08:00
|
|
|
method: 'POST',
|
|
|
|
headers: this.headers(),
|
|
|
|
})
|
|
|
|
}
|
2021-01-28 11:20:46 -08:00
|
|
|
|
2021-02-23 11:42:28 -08:00
|
|
|
// Files
|
2021-01-28 11:20:46 -08:00
|
|
|
|
2021-02-23 11:42:28 -08:00
|
|
|
// Returns fileId of uploaded file, or undefined on failure
|
2021-03-29 16:27:35 -07:00
|
|
|
async uploadFile(rootID: string, file: File): Promise<string | undefined> {
|
2021-02-23 11:42:28 -08:00
|
|
|
// IMPORTANT: We need to post the image as a form. The browser will convert this to a application/x-www-form-urlencoded POST
|
|
|
|
const formData = new FormData()
|
|
|
|
formData.append('file', file)
|
|
|
|
|
|
|
|
try {
|
|
|
|
const headers = this.headers() as Record<string, string>
|
|
|
|
|
|
|
|
// TIPTIP: Leave out Content-Type here, it will be automatically set by the browser
|
|
|
|
delete headers['Content-Type']
|
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
const response = await fetch(this.getBaseURL() + this.teamPath() + '/' + rootID + '/files', {
|
2021-02-23 11:42:28 -08:00
|
|
|
method: 'POST',
|
|
|
|
headers,
|
|
|
|
body: formData,
|
|
|
|
})
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
const text = await response.text()
|
|
|
|
Utils.log(`uploadFile response: ${text}`)
|
|
|
|
const json = JSON.parse(text)
|
|
|
|
|
2021-02-23 11:59:32 -08:00
|
|
|
return json.fileId
|
2021-02-23 11:42:28 -08:00
|
|
|
} catch (e) {
|
|
|
|
Utils.logError(`uploadFile json ERROR: ${e}`)
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
Utils.logError(`uploadFile ERROR: ${e}`)
|
|
|
|
}
|
|
|
|
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
2022-11-24 17:16:59 +05:30
|
|
|
async uploadAttachment(rootID: string, file: File): Promise<XMLHttpRequest | undefined> {
|
|
|
|
const formData = new FormData()
|
|
|
|
formData.append('file', file)
|
|
|
|
|
|
|
|
const xhr = new XMLHttpRequest()
|
|
|
|
|
|
|
|
xhr.open('POST', this.getBaseURL() + this.teamPath() + '/' + rootID + '/files', true)
|
|
|
|
const headers = this.headers() as Record<string, string>
|
|
|
|
delete headers['Content-Type']
|
|
|
|
|
|
|
|
xhr.setRequestHeader('Accept', 'application/json')
|
|
|
|
xhr.setRequestHeader('Authorization', this.token ? 'Bearer ' + this.token : '')
|
|
|
|
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
|
|
|
|
|
|
|
|
if (xhr.upload) {
|
|
|
|
xhr.upload.onprogress = () => {}
|
|
|
|
}
|
|
|
|
xhr.send(formData)
|
|
|
|
return xhr
|
|
|
|
}
|
|
|
|
|
|
|
|
async getFileInfo(boardId: string, fileId: string): Promise<FileInfo> {
|
|
|
|
let path = '/api/v2/files/teams/' + this.teamId + '/' + boardId + '/' + fileId + '/info'
|
|
|
|
const readToken = Utils.getReadToken()
|
|
|
|
if (readToken) {
|
|
|
|
path += `?read_token=${readToken}`
|
|
|
|
}
|
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
|
|
|
let fileInfo: FileInfo = {}
|
|
|
|
|
|
|
|
if (response.status === 200) {
|
|
|
|
fileInfo = this.getJson(response, {}) as FileInfo
|
|
|
|
} else if (response.status === 400) {
|
|
|
|
fileInfo = await this.getJson(response, {}) as FileInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
return fileInfo
|
|
|
|
}
|
|
|
|
|
2022-06-13 13:35:42 +05:30
|
|
|
async getFileAsDataUrl(boardId: string, fileId: string): Promise<FileInfo> {
|
2022-04-13 22:24:32 +02:00
|
|
|
let path = '/api/v2/files/teams/' + this.teamId + '/' + boardId + '/' + fileId
|
2021-10-01 11:29:44 -06:00
|
|
|
const readToken = Utils.getReadToken()
|
2021-03-29 16:27:35 -07:00
|
|
|
if (readToken) {
|
|
|
|
path += `?read_token=${readToken}`
|
|
|
|
}
|
2021-09-07 10:08:02 +05:30
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
2022-06-13 13:35:42 +05:30
|
|
|
let fileInfo: FileInfo = {}
|
|
|
|
|
|
|
|
if (response.status === 200) {
|
|
|
|
const blob = await response.blob()
|
|
|
|
fileInfo.url = URL.createObjectURL(blob)
|
|
|
|
} else if (response.status === 400) {
|
|
|
|
fileInfo = await this.getJson(response, {}) as FileInfo
|
2021-01-28 11:20:46 -08:00
|
|
|
}
|
2022-06-13 13:35:42 +05:30
|
|
|
|
|
|
|
return fileInfo
|
2021-01-28 11:20:46 -08:00
|
|
|
}
|
2021-06-04 18:53:15 +05:30
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
async getTeam(): Promise<Team | null> {
|
|
|
|
const path = this.teamPath()
|
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.getJson(response, null)
|
|
|
|
}
|
|
|
|
|
2022-09-14 15:11:50 +02:00
|
|
|
async getTeams(): Promise<Team[]> {
|
2022-03-22 15:24:34 +01:00
|
|
|
const path = this.teamsPath()
|
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
2022-09-14 15:11:50 +02:00
|
|
|
return this.getJson<Team[]>(response, [])
|
2022-03-22 15:24:34 +01:00
|
|
|
}
|
|
|
|
|
2022-09-09 20:56:44 -05:00
|
|
|
async getTeamUsers(excludeBots?: boolean): Promise<IUser[]> {
|
|
|
|
let path = this.teamPath() + '/users'
|
2022-09-14 15:11:50 +02:00
|
|
|
if (excludeBots) {
|
2022-09-09 20:56:44 -05:00
|
|
|
path += '?exclude_bots=true'
|
2022-09-14 15:11:50 +02:00
|
|
|
}
|
2021-09-07 10:08:02 +05:30
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
2021-06-04 18:53:15 +05:30
|
|
|
if (response.status !== 200) {
|
2021-07-14 21:22:40 +02:00
|
|
|
return []
|
2021-06-04 18:53:15 +05:30
|
|
|
}
|
2021-07-14 21:22:40 +02:00
|
|
|
return (await this.getJson(response, [])) as IUser[]
|
2021-06-04 18:53:15 +05:30
|
|
|
}
|
2021-09-08 10:22:03 +05:30
|
|
|
|
2022-09-09 20:56:44 -05:00
|
|
|
async searchTeamUsers(searchQuery: string, excludeBots?: boolean): Promise<IUser[]> {
|
|
|
|
let path = this.teamPath() + `/users?search=${searchQuery}`
|
2022-09-14 15:11:50 +02:00
|
|
|
if (excludeBots) {
|
2022-09-09 20:56:44 -05:00
|
|
|
path += '&exclude_bots=true'
|
2022-09-14 15:11:50 +02:00
|
|
|
}
|
2021-09-08 10:22:03 +05:30
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return []
|
|
|
|
}
|
2022-03-22 15:24:34 +01:00
|
|
|
return (await this.getJson(response, [])) as IUser[]
|
|
|
|
}
|
2021-09-08 10:22:03 +05:30
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
async getTeamTemplates(teamId?: string): Promise<Board[]> {
|
|
|
|
const path = this.teamPath(teamId) + '/templates'
|
|
|
|
return this.getBoardsWithPath(path)
|
2021-09-08 10:22:03 +05:30
|
|
|
}
|
2021-09-20 20:06:05 +05:30
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
async getBoards(): Promise<Board[]> {
|
|
|
|
const path = this.teamPath() + '/boards'
|
|
|
|
return this.getBoardsWithPath(path)
|
|
|
|
}
|
|
|
|
|
|
|
|
async getBoard(boardID: string): Promise<Board | undefined> {
|
2022-04-13 22:24:32 +02:00
|
|
|
let path = `/api/v2/boards/${boardID}`
|
2022-03-25 10:46:58 -06:00
|
|
|
const readToken = Utils.getReadToken()
|
|
|
|
if (readToken) {
|
|
|
|
path += `?read_token=${readToken}`
|
|
|
|
}
|
2022-03-22 15:24:34 +01:00
|
|
|
const response = await fetch(this.getBaseURL() + path, {
|
|
|
|
method: 'GET',
|
|
|
|
headers: this.headers(),
|
|
|
|
})
|
|
|
|
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.getJson<Board>(response, {} as Board)
|
|
|
|
}
|
|
|
|
|
|
|
|
async duplicateBoard(boardID: string, asTemplate: boolean, toTeam?: string): Promise<BoardsAndBlocks | undefined> {
|
|
|
|
let query = '?asTemplate=false'
|
|
|
|
if (asTemplate) {
|
|
|
|
query = '?asTemplate=true'
|
|
|
|
}
|
|
|
|
if (toTeam) {
|
|
|
|
query += `&toTeam=${encodeURIComponent(toTeam)}`
|
|
|
|
}
|
|
|
|
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = `/api/v2/boards/${boardID}/duplicate${query}`
|
2022-03-22 15:24:34 +01:00
|
|
|
const response = await fetch(this.getBaseURL() + path, {
|
|
|
|
method: 'POST',
|
|
|
|
headers: this.headers(),
|
|
|
|
})
|
|
|
|
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.getJson<BoardsAndBlocks>(response, {} as BoardsAndBlocks)
|
|
|
|
}
|
|
|
|
|
|
|
|
async duplicateBlock(boardID: string, blockID: string, asTemplate: boolean): Promise<Block[] | undefined> {
|
|
|
|
let query = '?asTemplate=false'
|
|
|
|
if (asTemplate) {
|
|
|
|
query = '?asTemplate=true'
|
|
|
|
}
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = `/api/v2/boards/${boardID}/blocks/${blockID}/duplicate${query}`
|
2022-03-22 15:24:34 +01:00
|
|
|
const response = await fetch(this.getBaseURL() + path, {
|
|
|
|
method: 'POST',
|
|
|
|
headers: this.headers(),
|
|
|
|
})
|
|
|
|
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.getJson<Block[]>(response, [] as Block[])
|
|
|
|
}
|
|
|
|
|
|
|
|
async getBlocksForBoard(teamId: string, boardId: string): Promise<Board[]> {
|
|
|
|
const path = this.teamPath(teamId) + `/boards/${boardId}`
|
|
|
|
return this.getBoardsWithPath(path)
|
|
|
|
}
|
|
|
|
|
|
|
|
async getBoardMembers(teamId: string, boardId: string): Promise<BoardMember[]> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = `/api/v2/boards/${boardId}/members`
|
2022-03-22 15:24:34 +01:00
|
|
|
return this.getBoardMembersWithPath(path)
|
|
|
|
}
|
|
|
|
|
|
|
|
async createBoard(board: Board): Promise<Response> {
|
|
|
|
Utils.log(`createBoard: ${board.title} [${board.type}]`)
|
|
|
|
return fetch(this.getBaseURL() + this.teamPath(board.teamId) + '/boards', {
|
|
|
|
method: 'POST',
|
|
|
|
headers: this.headers(),
|
|
|
|
body: JSON.stringify(board),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
async patchBoard(boardId: string, boardPatch: BoardPatch): Promise<Response> {
|
|
|
|
Utils.log(`patchBoard: ${boardId} board`)
|
|
|
|
const body = JSON.stringify(boardPatch)
|
2022-04-13 22:24:32 +02:00
|
|
|
return fetch(`${this.getBaseURL()}/api/v2/boards/${boardId}`, {
|
2022-03-22 15:24:34 +01:00
|
|
|
method: 'PATCH',
|
|
|
|
headers: this.headers(),
|
|
|
|
body,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
async deleteBoard(boardId: string): Promise<Response> {
|
|
|
|
Utils.log(`deleteBoard: ${boardId}`)
|
2022-04-13 22:24:32 +02:00
|
|
|
return fetch(`${this.getBaseURL()}/api/v2/boards/${boardId}`, {
|
2022-03-22 15:24:34 +01:00
|
|
|
method: 'DELETE',
|
|
|
|
headers: this.headers(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-09-14 15:11:50 +02:00
|
|
|
async getSidebarCategories(teamID: string): Promise<CategoryBoards[]> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = `/api/v2/teams/${teamID}/categories`
|
2022-03-22 15:24:34 +01:00
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
2022-09-14 15:11:50 +02:00
|
|
|
return (await this.getJson(response, [])) as CategoryBoards[]
|
2022-03-22 15:24:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
async createSidebarCategory(category: Category): Promise<Response> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = `/api/v2/teams/${category.teamID}/categories`
|
2022-03-22 15:24:34 +01:00
|
|
|
const body = JSON.stringify(category)
|
|
|
|
return fetch(this.getBaseURL() + path, {
|
|
|
|
method: 'POST',
|
|
|
|
headers: this.headers(),
|
|
|
|
body,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
async deleteSidebarCategory(teamID: string, categoryID: string): Promise<Response> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const url = `/api/v2/teams/${teamID}/categories/${categoryID}`
|
2022-03-22 15:24:34 +01:00
|
|
|
return fetch(this.getBaseURL() + url, {
|
|
|
|
method: 'DELETE',
|
|
|
|
headers: this.headers(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
async updateSidebarCategory(category: Category): Promise<Response> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = `/api/v2/teams/${category.teamID}/categories/${category.id}`
|
2022-03-22 15:24:34 +01:00
|
|
|
const body = JSON.stringify(category)
|
|
|
|
return fetch(this.getBaseURL() + path, {
|
|
|
|
method: 'PUT',
|
|
|
|
headers: this.headers(),
|
|
|
|
body,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-04-14 00:10:53 +02:00
|
|
|
async moveBoardToCategory(teamID: string, boardID: string, toCategoryID: string, fromCategoryID: string): Promise<Response> {
|
|
|
|
const url = `/api/v2/teams/${teamID}/categories/${toCategoryID || '0'}/boards/${boardID}`
|
2022-03-22 15:24:34 +01:00
|
|
|
const payload = {
|
|
|
|
fromCategoryID,
|
|
|
|
}
|
|
|
|
const body = JSON.stringify(payload)
|
|
|
|
|
|
|
|
return fetch(this.getBaseURL() + url, {
|
|
|
|
method: 'POST',
|
|
|
|
headers: this.headers(),
|
|
|
|
body,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-09-14 15:11:50 +02:00
|
|
|
async search(teamID: string, query: string): Promise<Board[]> {
|
2022-07-14 12:31:51 +02:00
|
|
|
const url = `${this.teamPath(teamID)}/boards/search?q=${encodeURIComponent(query)}`
|
|
|
|
const response = await fetch(this.getBaseURL() + url, {
|
|
|
|
method: 'GET',
|
|
|
|
headers: this.headers(),
|
|
|
|
})
|
|
|
|
|
2022-08-05 18:32:19 +02:00
|
|
|
if (response.status !== 200) {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
2022-09-14 15:11:50 +02:00
|
|
|
return (await this.getJson(response, [])) as Board[]
|
2022-08-05 18:32:19 +02:00
|
|
|
}
|
|
|
|
|
2022-09-14 15:11:50 +02:00
|
|
|
async searchLinkableBoards(teamID: string, query: string): Promise<Board[]> {
|
2022-08-05 18:32:19 +02:00
|
|
|
const url = `${this.teamPath(teamID)}/boards/search/linkable?q=${encodeURIComponent(query)}`
|
|
|
|
const response = await fetch(this.getBaseURL() + url, {
|
|
|
|
method: 'GET',
|
|
|
|
headers: this.headers(),
|
|
|
|
})
|
|
|
|
|
2022-07-14 12:31:51 +02:00
|
|
|
if (response.status !== 200) {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
2022-09-14 15:11:50 +02:00
|
|
|
return (await this.getJson(response, [])) as Board[]
|
2022-07-14 12:31:51 +02:00
|
|
|
}
|
|
|
|
|
2022-09-14 15:11:50 +02:00
|
|
|
async searchAll(query: string): Promise<Board[]> {
|
2022-07-14 12:31:51 +02:00
|
|
|
const url = `/api/v2/boards/search?q=${encodeURIComponent(query)}`
|
2022-03-22 15:24:34 +01:00
|
|
|
const response = await fetch(this.getBaseURL() + url, {
|
|
|
|
method: 'GET',
|
|
|
|
headers: this.headers(),
|
|
|
|
})
|
|
|
|
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
2022-09-14 15:11:50 +02:00
|
|
|
return (await this.getJson(response, [])) as Board[]
|
2021-09-20 20:06:05 +05:30
|
|
|
}
|
2021-12-10 10:46:37 -05:00
|
|
|
|
2022-09-14 15:11:50 +02:00
|
|
|
async getUserBlockSubscriptions(userId: string): Promise<Subscription[]> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = `/api/v2/subscriptions/${userId}`
|
2021-12-10 10:46:37 -05:00
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
|
|
|
return (await this.getJson(response, [])) as Subscription[]
|
|
|
|
}
|
2022-02-28 16:58:16 +05:30
|
|
|
|
2022-07-07 16:46:53 +02:00
|
|
|
async searchUserChannels(teamId: string, searchQuery: string): Promise<Channel[] | undefined> {
|
|
|
|
const path = `/api/v2/teams/${teamId}/channels?search=${searchQuery}`
|
|
|
|
const response = await fetch(this.getBaseURL() + path, {
|
|
|
|
headers: this.headers(),
|
|
|
|
method: 'GET',
|
|
|
|
})
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
return (await this.getJson(response, [])) as Channel[]
|
|
|
|
}
|
|
|
|
|
|
|
|
async getChannel(teamId: string, channelId: string): Promise<Channel | undefined> {
|
|
|
|
const path = `/api/v2/teams/${teamId}/channels/${channelId}`
|
|
|
|
const response = await fetch(this.getBaseURL() + path, {
|
|
|
|
headers: this.headers(),
|
|
|
|
method: 'GET',
|
|
|
|
})
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
return (await this.getJson(response, {})) as Channel
|
|
|
|
}
|
|
|
|
|
2022-02-28 16:58:16 +05:30
|
|
|
// onboarding
|
2022-03-22 15:24:34 +01:00
|
|
|
async prepareOnboarding(teamId: string): Promise<PrepareOnboardingResponse | undefined> {
|
2022-04-13 22:24:32 +02:00
|
|
|
const path = `/api/v2/teams/${teamId}/onboard`
|
2022-02-28 16:58:16 +05:30
|
|
|
const response = await fetch(this.getBaseURL() + path, {
|
|
|
|
headers: this.headers(),
|
|
|
|
method: 'POST',
|
|
|
|
})
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
2022-03-22 15:24:34 +01:00
|
|
|
return (await this.getJson(response, {})) as PrepareOnboardingResponse
|
2022-02-28 16:58:16 +05:30
|
|
|
}
|
2022-06-20 20:39:20 +02:00
|
|
|
|
2022-06-29 18:05:24 +05:30
|
|
|
async notifyAdminUpgrade(): Promise<void> {
|
2022-07-13 11:49:05 +05:30
|
|
|
const path = `${this.teamPath()}/notifyadminupgrade`
|
2022-06-29 18:05:24 +05:30
|
|
|
await fetch(this.getBaseURL() + path, {
|
|
|
|
headers: this.headers(),
|
|
|
|
method: 'POST',
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-06-20 20:39:20 +02:00
|
|
|
async getBoardsCloudLimits(): Promise<BoardsCloudLimits | undefined> {
|
|
|
|
const path = '/api/v2/limits'
|
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
const limits = (await this.getJson(response, {})) as BoardsCloudLimits
|
|
|
|
Utils.log(`Cloud limits: cards=${limits.cards} views=${limits.views}`)
|
|
|
|
return limits
|
|
|
|
}
|
[MM-43781] Feature: boards insights (#3005)
* Add boilerplate functions and handlers for boards insights
* Fix function signatures to add 'duration' parameter, fix where clauses in db queries
* Fix where clause to include boards of which userId in parameter is a member
* Modify queries to work with sqlite, postgres, mysql
* Integration tests, and results of make generate
* Lint Fixes
* Add icons to board insights
* Lint fixes
* Format insights queries without squirrel to fix parameterization issues
* Add tests for sqlstore utility functions
* Improve team insights tests by creating 2 boards
* Refactor endpoints/app to adhere to developments in 7.0 release
* Refactor queries to use squirrel
* Lint fixes
* Fix client, integration tests
* Remove old integration tests
* Add storetests, refactor functions to handle authorized board_ids
* Make queries compatible with mysql, sqlite
* Add app tests
* Fix lint errors
* Revert makefile changes, fix docstring in api
* Lint fixes and doc correction suggested by @wiggin77
* Fix mock store call count error
* adding client code
* Make the following changes
- use serviceAPI to get user.Timezone
- rename licenseAndGuestUserCheck to insightPermissionGate, and handle returned error better
- validate page, perPage parameters aren't < 0
* Lint fix
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Co-authored-by: Benjamin Cooke <benjamincooke@Benjamins-MacBook-Pro.local>
2022-08-08 11:42:02 +05:30
|
|
|
|
2022-10-25 14:28:00 -06:00
|
|
|
async getSiteStatistics(): Promise<BoardSiteStatistics | undefined> {
|
|
|
|
const path = '/api/v2/statistics'
|
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
const stats = (await this.getJson(response, {})) as BoardSiteStatistics
|
|
|
|
Utils.log(`Site Statistics: cards=${stats.card_count} boards=${stats.board_count}`)
|
|
|
|
return stats
|
|
|
|
}
|
|
|
|
|
[MM-43781] Feature: boards insights (#3005)
* Add boilerplate functions and handlers for boards insights
* Fix function signatures to add 'duration' parameter, fix where clauses in db queries
* Fix where clause to include boards of which userId in parameter is a member
* Modify queries to work with sqlite, postgres, mysql
* Integration tests, and results of make generate
* Lint Fixes
* Add icons to board insights
* Lint fixes
* Format insights queries without squirrel to fix parameterization issues
* Add tests for sqlstore utility functions
* Improve team insights tests by creating 2 boards
* Refactor endpoints/app to adhere to developments in 7.0 release
* Refactor queries to use squirrel
* Lint fixes
* Fix client, integration tests
* Remove old integration tests
* Add storetests, refactor functions to handle authorized board_ids
* Make queries compatible with mysql, sqlite
* Add app tests
* Fix lint errors
* Revert makefile changes, fix docstring in api
* Lint fixes and doc correction suggested by @wiggin77
* Fix mock store call count error
* adding client code
* Make the following changes
- use serviceAPI to get user.Timezone
- rename licenseAndGuestUserCheck to insightPermissionGate, and handle returned error better
- validate page, perPage parameters aren't < 0
* Lint fix
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Co-authored-by: Benjamin Cooke <benjamincooke@Benjamins-MacBook-Pro.local>
2022-08-08 11:42:02 +05:30
|
|
|
// insights
|
|
|
|
async getMyTopBoards(timeRange: string, page: number, perPage: number, teamId: string): Promise<TopBoardResponse | undefined> {
|
|
|
|
const path = `/api/v2/users/me/boards/insights?time_range=${timeRange}&page=${page}&per_page=${perPage}&team_id=${teamId}`
|
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
return (await this.getJson(response, {})) as TopBoardResponse
|
|
|
|
}
|
|
|
|
|
|
|
|
async getTeamTopBoards(timeRange: string, page: number, perPage: number, teamId: string): Promise<TopBoardResponse | undefined> {
|
|
|
|
const path = `/api/v2/teams/${teamId}/boards/insights?time_range=${timeRange}&page=${page}&per_page=${perPage}`
|
|
|
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
|
|
|
if (response.status !== 200) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
return (await this.getJson(response, {})) as TopBoardResponse
|
|
|
|
}
|
2022-11-15 16:59:07 +01:00
|
|
|
|
|
|
|
async moveBlockTo(blockId: string, where: 'before'|'after', dstBlockId: string): Promise<Response> {
|
|
|
|
return fetch(`${this.getBaseURL()}/api/v2/content-blocks/${blockId}/moveto/${where}/${dstBlockId}`, {
|
|
|
|
method: 'POST',
|
|
|
|
headers: this.headers(),
|
|
|
|
body: '{}',
|
|
|
|
})
|
|
|
|
}
|
2020-10-08 09:21:27 -07:00
|
|
|
}
|
|
|
|
|
2021-03-30 14:04:00 -07:00
|
|
|
const octoClient = new OctoClient()
|
2020-10-15 16:57:43 +02:00
|
|
|
|
2021-03-30 14:04:00 -07:00
|
|
|
export {OctoClient}
|
|
|
|
export default octoClient
|