• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

evolvedbinary / prosemirror-lwdita / 7c3793a3-8d63-4321-895b-dec10bb58f05

30 Oct 2024 11:05AM UTC coverage: 43.646% (-0.4%) from 44.032%
7c3793a3-8d63-4321-895b-dec10bb58f05

Pull #479

circleci

marmoure
bugfix] only replace relative urls
Pull Request #479: Images should point to the base url

175 of 374 branches covered (46.79%)

Branch coverage included in aggregate %.

1 of 7 new or added lines in 1 file covered. (14.29%)

1 existing line in 1 file now uncovered.

323 of 767 relevant lines covered (42.11%)

26.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

65.63
/packages/prosemirror-lwdita/src/github-integration/github.plugin.ts
1
/*!
2
Copyright (C) 2020 Evolved Binary
3

4
This program is free software: you can redistribute it and/or modify
5
it under the terms of the GNU Affero General Public License as
6
published by the Free Software Foundation, either version 3 of the
7
License, or (at your option) any later version.
8

9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
GNU Affero General Public License for more details.
13

14
You should have received a copy of the GNU Affero General Public License
15
along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
*/
17

18
import { xditaToJdita } from "@evolvedbinary/lwdita-xdita";
1✔
19
import { document as jditaToProsemirrorJson } from "../document";
1✔
20
import { showErrorPage } from "./request";
1✔
21
import { showToast } from "../toast";
1✔
22
import { Config } from "../config";
23
import { Localization } from "@evolvedbinary/prosemirror-lwdita-localization";
24

25
/**
26
 * Fetches the raw content of a document from a GitHub repository.
27
 *
28
 * @param config - configuration
29
 * @param ghrepo - The GitHub repository in the format "owner/repo".
30
 * @param source - The path to the file within the repository.
31
 * @param branch - The branch from which to fetch the document.
32
 * @returns A promise that resolves to the raw content of the document as a string.
33
 *
34
 * @remarks
35
 * This function currently fetches the document from the 'main' branch of the repository.
36
 * should use the GitHub API to dynamically determine the default branch of the repository.
37
 */
38
export const fetchRawDocumentFromGitHub = async (config: Config, ghrepo: string, source: string, branch: string): Promise<string> => {
2✔
39
  // GitHub changes the raw api URL from `main` to `refs/heads/main` 
40
  // https://raw.githubusercontent.com/evolvedbinary/prosemirror-lwdita/main/packages/prosemirror-lwdita-demo/example-xdita/02-short-file.xml
41
  // https://raw.githubusercontent.com/evolvedbinary/prosemirror-lwdita/refs/heads/main/packages/prosemirror-lwdita-demo/example-xdita/02-short-file.xml
42
  const url = `https://raw.githubusercontent.com/${ghrepo}/refs/heads/${branch}/${source}`;
2✔
43
  const response = await fetch(url);
2✔
44

45
  if (!response.ok) {
2✔
46
    showErrorPage(config, 'fileNotFound', '', response.statusText);
1✔
47
  }
48
  //TODO: Handle errors
49
  return response.text();
1✔
50
};
51

52
/**
53
 * Transforms a raw GitHub document into a ProseMirror state save.
54
 *
55
 * @param rawDocument - The raw xdita document as a string.
56
 * @returns A promise that resolves to a record containing the ProseMirror state save.
57
 */
58
// eslint-disable-next-line @typescript-eslint/no-explicit-any
59
export const transformGitHubDocumentToProsemirrorJson = async (rawDocument: string): Promise<Record<string, any>> => {
1✔
60
  // convert the raw xdita document to jdita
61
  const jdita = await xditaToJdita(rawDocument);
1✔
62

63
  // convert the jdita document to prosemirror state save
64
  const prosemirrorJson = await jditaToProsemirrorJson(jdita);
1✔
65

66
  return prosemirrorJson;
1✔
67
};
68

69
/**
70
 * Fetches a raw document from a GitHub repository and transforms it into a ProseMirror JSON document.
71
 *
72
 * @param config - configuration
73
 * @param ghrepo - The GitHub repository from which to fetch the document.
74
 * @param source - The source path of the document within the repository.
75
 * @param branch - The branch from which to fetch the document.
76
 * @returns A promise that resolves to the transformed ProseMirror JSON document.
77
 */
78
export const fetchAndTransform = async (config: Config, ghrepo: string, source: string, branch: string, referer: string) => {
1✔
79
  const rawDoc = await fetchRawDocumentFromGitHub(config, ghrepo, source, branch);
×
80
  // update the document with the relative path
81

NEW
82
  const updatedDoc = rawDoc.replace(/href="([^"]+)"/g, (match, url) => {
×
83
    // avoid changing the url if it is already an absolute url
NEW
84
    if(URL.parse(url)?.origin !== null) {
×
NEW
85
      return match;
×
86
    }
87
    // get the base url from the referer
NEW
88
    const baseUrl = referer.split('/').slice(0, -1).join('/');
×
89
    // return the updated href url
NEW
90
    return `href="${baseUrl}${url}"`;
×
91
  });
92

NEW
93
  const jsonDoc = await transformGitHubDocumentToProsemirrorJson(updatedDoc);
×
UNCOV
94
  return jsonDoc;
×
95
};
96

97
/**
98
 * Exchanges an OAuth code for an access token.
99
 *
100
 * @param config - configuration
101
 * @param localization - localization
102
 * @param code - The OAuth code to exchange for an access token.
103
 * @returns A promise that resolves to the access token as a string.
104
 * @throws Will throw an error if the fetch request fails or if the response is not in the expected format.
105
 */
106
export const exchangeOAuthCodeForAccessToken = async (config: Config, localization: Localization, code: string): Promise<{token: string, installation: boolean}> => {
1✔
107
  // build the URL to exchange the code for an access token
108
  const url = config.server.api.baseUrl + config.server.api.endpoint.token + `?code=${code}`;
×
109
  // fetch the access token
110
  const response = await fetch(url);
×
111

112
  // TODO (AvC): This error type might be changed to be more specific depending on
113
  // further error handling
114
  if (!response.ok) {
×
115
    showToast(localization.t("error.toastGitHubToken") + response.statusText, 'error');
×
116
  }
117

118
  const json = await response.json();
×
119
  //TODO: Handle errors
120
  return {
×
121
    token: json.token,
122
    installation: json.installation
123
  };
124
};
125

126
/**
127
 * Fetches user information from the backend API.
128
 *
129
 * @param config - configuration
130
 * @param localization - localization
131
 * @param token - The authorization token to access the GitHub API.
132
 * @returns A promise that resolves to a record containing user information.
133
 */
134
export const getUserInfo = async (config: Config, localization: Localization, token: string): Promise<Record<string, string>> => {
3✔
135
  const url = config.server.api.baseUrl + config.server.api.endpoint.user;
3✔
136
  const response = await fetch(url, {
3✔
137
    headers: {
138
      'authorization': `Bearer ${token}`
139
    }
140
  });
141

142
  // TODO (AvC): This error type might be changed to be more specific depending on
143
  // further error handling
144
  if (!response.ok) {
3✔
145
    showToast(localization.t("error.toastGitHubUserEndpoint") + response.statusText, 'error');
1✔
146
  }
147
  const json = await response.json();
2✔
148
  return json;
2✔
149
};
150

151
/**
152
 * Publishes a document to a specified GitHub repository.
153
 * Makes a POST request to the `/api/github/integration` endpoint with the necessary details to create a pull request.
154
 *
155
 * @param config - configuration
156
 * @param localization - localization
157
 * @param ghrepo - The GitHub repository in the format "owner/repo".
158
 * @param source - The path to the source document.
159
 * @param branch - The branch used as base for the PR.
160
 * @param title - The title of the pull request and the commit message.
161
 * @param desc - The description of the pull request.
162
 * @param changedDocument - The content of the changed document.
163
 * @returns A promise that resolves when the document has been published.
164
 */
165
export const createPrFromContribution = async (config: Config, localization: Localization, ghrepo: string, source: string, branch: string, changedDocument: string, title: string, desc: string): Promise<string> => {
1✔
166
  const authenticatedUserInfo = await getUserInfo(config, localization, localStorage.getItem('token') as string);
1✔
167

168
  const owner = ghrepo.split('/')[0];
1✔
169
  const repo = ghrepo.split('/')[1];
1✔
170
  const newOwner = authenticatedUserInfo.login;
1✔
171
  const date = new Date();
1✔
172
  const newBranch = config.git.branchPrefix + `${date.getFullYear()}${date.getMonth()}${date.getDate()}${date.getHours()}${date.getMinutes()}${date.getSeconds()}`;
1✔
173
  const commitMessage = title;
1✔
174
  const path = source;
1✔
175
  const content = changedDocument;
1✔
176
  const change = {
1✔
177
    path,
178
    content
179
  };
180
  const body = `${desc}` + config.git.commitMessageSuffix;
1✔
181
  // get the token from the local storage
182
  const token = localStorage.getItem('token');
1✔
183
  // make a post request to  /api/github/integration
184
  const response = await fetch(config.server.api.baseUrl + config.server.api.endpoint.integration, {
1✔
185
    method: 'POST',
186
    headers: {
187
      'Content-Type': 'application/json',
188
      'Authorization': `Bearer ${token}`
189
    },
190
    body: JSON.stringify({
191
      owner,
192
      repo,
193
      newOwner,
194
      branch,
195
      newBranch,
196
      commitMessage,
197
      change,
198
      title,
199
      body
200
    })
201
  });
202

203
  // TODO (AvC): This error type might be changed to be more specific depending on
204
  // further error handling
205
  if (!response.ok) {
1!
206
    showToast(localization.t("error.toastGitHubPR") + response.statusText, 'error');
×
207
  }
208

209
  const json = await response.json();
1✔
210
  return json.url;
1✔
211
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc