diff --git a/packages/@vssue/api-coding/.gitignore b/packages/@vssue/api-coding/.gitignore new file mode 100644 index 00000000..729da3f9 --- /dev/null +++ b/packages/@vssue/api-coding/.gitignore @@ -0,0 +1,2 @@ +lib/ +types/ diff --git a/packages/@vssue/api-coding/.npmignore b/packages/@vssue/api-coding/.npmignore new file mode 100644 index 00000000..61a6ae83 --- /dev/null +++ b/packages/@vssue/api-coding/.npmignore @@ -0,0 +1,4 @@ +__mocks__/ +__tests__/ +src/ +tsconfig.json diff --git a/packages/@vssue/api-coding/Readme.md b/packages/@vssue/api-coding/Readme.md new file mode 100644 index 00000000..0aef4db9 --- /dev/null +++ b/packages/@vssue/api-coding/Readme.md @@ -0,0 +1,12 @@ +# @vssue/api-coding + +> Vssue API for Coding + +[__Live Demo and Docs__](https://vssue.js.org) + +[__Github Repo__](https://github.com/meteorlxy/vssue) + +## Features + +- Comments sortable: `false` +- Comments reactions: `false` diff --git a/packages/@vssue/api-coding/package.json b/packages/@vssue/api-coding/package.json new file mode 100644 index 00000000..4cc6f460 --- /dev/null +++ b/packages/@vssue/api-coding/package.json @@ -0,0 +1,32 @@ +{ + "name": "@vssue/api-coding", + "version": "0.7.0", + "description": "Vssue api for coding", + "license": "MIT", + "main": "lib/index.js", + "types": "types/index.d.ts", + "keywords": [ + "comment", + "issue", + "vue" + ], + "author": "meteorlxy ", + "homepage": "https://vssue.js.org", + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/meteorlxy/vssue.git" + }, + "bugs": { + "url": "https://github.com/meteorlxy/vssue/issues" + }, + "scripts": { + "build": "rimraf lib types && tsc -p tsconfig.json" + }, + "dependencies": { + "@vssue/utils": "^0.7.0", + "axios": "^0.18.0" + } +} diff --git a/packages/@vssue/api-coding/src/index.ts b/packages/@vssue/api-coding/src/index.ts new file mode 100644 index 00000000..8801532e --- /dev/null +++ b/packages/@vssue/api-coding/src/index.ts @@ -0,0 +1,334 @@ +import { VssueAPI } from 'vssue' + +import axios, { + AxiosInstance, + AxiosRequestConfig, +} from 'axios' + +import { + buildQuery, + buildURL, + concatURL, + getCleanURL, + parseQuery, +} from '@vssue/utils' + +import { + normalizeUser, + normalizeIssue, + normalizeComment, +} from './utils' + +/** + * Coding API + * + * @see https://open.coding.net/references/api/ + * @see https://open.coding.net/references/oauth/ + * @see https://coding.net/help/doc/account/oauth.html + */ +export default class Coding implements VssueAPI.Instance { + baseURL: string + owner: string + repo: string + clientId: string + clientSecret: string + state: string + proxy: string | ((url: string) => string) + $http: AxiosInstance + + constructor ({ + baseURL = 'https://coding.net', + owner, + repo, + clientId, + clientSecret, + state, + proxy, + }: VssueAPI.Options) { + this.baseURL = baseURL + this.owner = owner + this.repo = repo + + this.clientId = clientId + this.clientSecret = clientSecret + this.state = state + this.proxy = proxy + + this.$http = axios.create({ + baseURL, + headers: { + 'Accept': 'application/json', + }, + }) + + this.$http.interceptors.response.use(response => { + if (response.data.code) { + return Promise.reject(Object.values(response.data.msg)[0]) + } + return response + }) + } + + /** + * The platform api info + */ + get platform (): VssueAPI.Platform { + return { + name: 'Coding', + link: this.baseURL, + version: '', + meta: { + reactable: false, + sortable: true, + }, + } + } + + /** + * Redirect to the authorization page of platform. + * + * @see https://open.coding.net/references/oauth/ + * @see https://coding.net/help/doc/account/oauth.html#i-3 + */ + redirectAuth (): void { + window.location.href = buildURL(concatURL(this.baseURL, 'oauth_authorize.html'), { + client_id: this.clientId, + redirect_uri: window.location.href, + response_type: 'code', + }) + } + + /** + * Handle authorization. + * + * @return A string for access token, `null` for no authorization code + * + * @see https://open.coding.net/references/oauth/ + * @see https://coding.net/help/doc/account/oauth.html#i-3 + * + * @remarks + * If the `code` exists in the query, remove them from query, and try to get the access token. + */ + async handleAuth (): Promise { + const query = parseQuery(window.location.search) + if (query.code) { + const code = query.code + delete query.code + const replaceURL = buildURL(getCleanURL(window.location.href), query) + window.location.hash + window.history.replaceState(null, '', replaceURL) + const accessToken = await this.getAccessToken({ code }) + return accessToken + } + return null + } + + /** + * Get user access token via `code` + * + * @param options.code - The code from the query + * + * @return User access token + * + * @see https://open.coding.net/references/oauth/ + * @see https://coding.net/help/doc/account/oauth.html#i-3 + */ + async getAccessToken ({ + code, + }: { + code: string + }): Promise { + const originalURL = buildURL(concatURL(this.baseURL, 'api/oauth/access_token'), { + client_id: this.clientId, + client_secret: this.clientSecret, + grant_type: 'authorization_code', + code, + }) + const proxyURL = typeof this.proxy === 'function' + ? this.proxy(originalURL) + : this.proxy + const { data } = await this.$http.post(proxyURL) + return data.access_token + } + + /** + * Get the logined user with access token. + * + * @param options.accessToken - User access token + * + * @return The user + * + * @see https://open.coding.net/references/api/ + * @see https://coding.net/help/doc/account/oauth.html#i-5 + */ + async getUser ({ + accessToken, + }: { + accessToken: VssueAPI.AccessToken + }): Promise { + const { data } = await this.$http.get('api/account/current_user', { + headers: { 'Authorization': `token ${accessToken}` }, + }) + return normalizeUser(data, this.baseURL) + } + + /** + * Get issue of this page according to the issue id or the issue title + * + * @param options.accessToken - User access token + * @param options.issueId - The id of issue + * @param options.issueTitle - The title of issue + * + * @return The raw response of issue + * + * @see https://open.coding.net/references/api/ + * @see https://coding.net/help/doc/account/oauth.html#_id + */ + async getIssue ({ + accessToken, + issueId, + issueTitle, + }: { + accessToken: VssueAPI.AccessToken + issueId?: string | number + issueTitle?: string + }): Promise { + throw new Error('501 Not Implemented') + } + + /** + * Create a new issue + * + * @param options.accessToken - User access token + * @param options.title - The title of issue + * @param options.content - The content of issue + * + * @return The created issue + * + * @see https://open.coding.net/references/api/ + * @see https://coding.net/help/doc/account/oauth.html#i-8 + */ + async postIssue ({ + accessToken, + title, + content, + }: { + accessToken: VssueAPI.AccessToken + title: string + content: string + }): Promise { + throw new Error('501 Not Implemented') + } + + /** + * Get comments of this page according to the issue id + * + * @param options.accessToken - User access token + * @param options.issueId - The id of issue + * @param options.query - The query parameters + * + * @return The comments + * + * @see https://open.coding.net/references/api/ + */ + async getComments ({ + accessToken, + issueId, + query: { + page = 1, + perPage = 10, + sort = 'desc', + } = {}, + }: { + accessToken: VssueAPI.AccessToken + issueId: string | number + query?: Partial + }): Promise { + throw new Error('501 Not Implemented') + } + + /** + * Create a new comment + * + * @param options.accessToken - User access token + * @param options.issueId - The id of issue + * @param options.content - The content of comment + * + * @return The created comment + * + * @see https://open.coding.net/references/api/ + */ + async postComment ({ + accessToken, + issueId, + content, + }: { + accessToken: VssueAPI.AccessToken + issueId: string | number + content: string + }): Promise { + throw new Error('501 Not Implemented') + } + + /** + * Edit a comment + * + * @param options.accessToken - User access token + * @param options.issueId - The id of issue + * @param options.commentId - The id of comment + * @param options.content - The content of comment + * + * @return The edited comment + * + * @see https://open.coding.net/references/api/ + */ + async putComment ({ + accessToken, + issueId, + commentId, + content, + }: { + accessToken: VssueAPI.AccessToken + issueId: string | number + commentId: string | number + content: string + }): Promise { + throw new Error('501 Not Implemented') + } + + /** + * Delete a comment + * + * @param options.accessToken - User access token + * @param options.issueId - The id of issue + * @param options.commentId - The id of comment + * + * @return `true` if succeed, `false` if failed + * + * @see https://open.coding.net/references/api/ + */ + async deleteComment ({ + accessToken, + issueId, + commentId, + }: { + accessToken: VssueAPI.AccessToken + issueId: string | number + commentId: string | number + }): Promise { + throw new Error('501 Not Implemented') + } + + /** + * Coding does not support reactions now + */ + async getCommentReactions (): Promise { + throw new Error('501 Not Implemented') + } + + /** + * Coding does not support reactions now + */ + async postCommentReaction (): Promise { + throw new Error('501 Not Implemented') + } +} diff --git a/packages/@vssue/api-coding/src/utils.ts b/packages/@vssue/api-coding/src/utils.ts new file mode 100644 index 00000000..234a4348 --- /dev/null +++ b/packages/@vssue/api-coding/src/utils.ts @@ -0,0 +1,37 @@ +import { VssueAPI } from 'vssue' +import { concatURL } from '@vssue/utils' + +export function normalizeUser (user: any, baseURL: string): VssueAPI.User { + return { + username: user.name, + avatar: concatURL(baseURL, user.avatar), + homepage: concatURL(baseURL, user.path), + } +} + +export function normalizeIssue (issue: any, baseURL: string): VssueAPI.Issue { + return { + id: issue.id, + title: issue.content, + content: issue.has_description, + link: concatURL(baseURL, `${issue.project.project_path}/task/${issue.id}`), + } +} + +export function normalizeComment (comment: any, baseURL: string): VssueAPI.Comment { + return { + id: comment.id, + content: comment.content.html, + contentRaw: comment.content.raw, + author: normalizeUser(comment.user, baseURL), + createdAt: comment.created_on, + updatedAt: comment.updated_on, + reactions: null, + } +} + +export default { + normalizeUser, + normalizeIssue, + normalizeComment, +} diff --git a/packages/@vssue/api-coding/tsconfig.json b/packages/@vssue/api-coding/tsconfig.json new file mode 100644 index 00000000..333b766c --- /dev/null +++ b/packages/@vssue/api-coding/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "declarationDir": "./types", + "sourceMap": false, + "inlineSourceMap": true, + "inlineSources": true + }, + "include": [ + "./src" + ] + } + \ No newline at end of file diff --git a/packages/docs/src/guide/supported-platforms.md b/packages/docs/src/guide/supported-platforms.md index 8ff3c7b2..c26a4cfe 100644 --- a/packages/docs/src/guide/supported-platforms.md +++ b/packages/docs/src/guide/supported-platforms.md @@ -53,3 +53,14 @@ Guide: [Set up Bitbucket OAuth Cousumer](./bitbucket.md) - after login, the access token expires in one hour - cannot post emoji reactions to comments (heart, like, unlike, etc.) - Developer Reference: [Official Docs](https://developer.atlassian.com/bitbucket/api/2/reference) + +## Coding + +Guide: []() + +#### Coding API + +- Vssue API package: [@vssue/api-coding](https://www.npmjs.com/package/@vssue/api-coding) +- Features or drawbacks: + - +- Developer Reference: [Official Docs](https://coding.net/help/doc/account/oauth.html) diff --git a/packages/docs/src/zh/guide/supported-platforms.md b/packages/docs/src/zh/guide/supported-platforms.md index 171e7992..8d259315 100644 --- a/packages/docs/src/zh/guide/supported-platforms.md +++ b/packages/docs/src/zh/guide/supported-platforms.md @@ -53,3 +53,14 @@ - 登陆后 Access Token 在 1 小时后过期 - 不能对评论做出 emoji 响应 (喜欢、点赞、踩 等) - 开发者参考: [官方文档](https://developer.atlassian.com/bitbucket/api/2/reference) + +## Coding + +指南: []() + +#### Coding API + +- Vssue API 包: [@vssue/api-coding](https://www.npmjs.com/package/@vssue/api-coding) +- 特点 / 缺点: + - +- 开发者参考: [官方文档](https://coding.net/help/doc/account/oauth.html)