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

evolvedbinary / prosemirror-lwdita / 2fdac73d-ca03-4003-9104-c22462ddde33

09 Jul 2025 09:41AM UTC coverage: 44.455% (+0.4%) from 44.081%
2fdac73d-ca03-4003-9104-c22462ddde33

Pull #573

circleci

marmoure
[bugfix] decode the referer from the state
Pull Request #573: Fix redirect to error pages

170 of 352 branches covered (48.3%)

Branch coverage included in aggregate %.

11 of 25 new or added lines in 2 files covered. (44.0%)

3 existing lines in 2 files now uncovered.

315 of 739 relevant lines covered (42.63%)

27.38 hits per line

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

75.47
/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, URLParams } from "./request";
1✔
21
import { Config } from "../config";
22
import { Localization } from "@evolvedbinary/prosemirror-lwdita-localization";
23
import urijs from "urijs";
1✔
24

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

44
  if (!response.ok) {
2✔
45
    showErrorPage(config, 'fileNotFoundError', urlParams.referer);
1✔
46
  }
47

48
  return response.text();
1✔
49
};
50

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

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

65
  return prosemirrorJson;
1✔
66
};
67

68
/**
69
 * Fetches a raw document from a GitHub repository and transforms it into a ProseMirror JSON document.
70
 *
71
 * @param config - configuration
72
 * @param urlParams - Object containing ghrepo, source, branch.
73
 * @returns A promise that resolves to the transformed ProseMirror JSON document.
74
 */
75
export const fetchAndTransform = async (config: Config, urlParams: URLParams) => {
1✔
76

NEW
77
  const rawDoc = await fetchRawDocumentFromGitHub(config, urlParams);
×
78
  
79
  // update the document with the relative path
UNCOV
80
  const updatedDoc = rawDoc.replace(/href="([^"]+)"/g, (_match, url) => {
×
81
    // https://www.npmjs.com/package/urijs
NEW
82
    return `href="${urijs(url).absoluteTo(urlParams.referrer).href()}"`;
×
83
  });
84

NEW
85
  try {
×
NEW
86
    const jsonDoc = await transformGitHubDocumentToProsemirrorJson(updatedDoc);
×
NEW
87
    return jsonDoc;
×
88
  } catch (_error) {
NEW
89
    showErrorPage(config, 'incompatibleXditaFile', urlParams.referer);
×
90
  }
91
};
92

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

109
  return {
×
110
    token: json.token,
111
    installation: json.installation
112
  };
113
};
114

115
/**
116
 * Fetches user information from the backend API.
117
 *
118
 * @param config - configuration
119
 * @param _localization - localization
120
 * @param token - The authorization token to access the GitHub API.
121
 * @returns A promise that resolves to a record containing user information.
122
 */
123
export const getUserInfo = async (config: Config, _localization: Localization, token: string): Promise<Record<string, string>> => {
3✔
124
  const url = config.server.api.baseUrl + config.server.api.endpoint.user;
3✔
125
  const response = await fetch(url, {
3✔
126
    headers: {
127
      'authorization': `Bearer ${token}`
128
    }
129
  });
130

131
  const json = await response.json();
3✔
132
  return json;
2✔
133
};
134

135
/**
136
 * Publishes a document to a specified GitHub repository.
137
 * Makes a POST request to the `/api/github/integration` endpoint with the necessary details to create a pull request.
138
 *
139
 * @param config - configuration
140
 * @param localization - localization
141
 * @param ghrepo - The GitHub repository in the format "owner/repo".
142
 * @param source - The path to the source document.
143
 * @param branch - The branch used as base for the PR.
144
 * @param title - The title of the pull request and the commit message.
145
 * @param desc - The description of the pull request.
146
 * @param changedDocument - The content of the changed document.
147
 * @returns A promise that resolves when the document has been published.
148
 */
149
export const createPrFromContribution = async (config: Config, localization: Localization, ghrepo: string, source: string, branch: string, changedDocument: string, title: string, desc: string): Promise<string> => {
1✔
150
  const authenticatedUserInfo = await getUserInfo(config, localization, localStorage.getItem('token') as string);
1✔
151

152
  const owner = ghrepo.split('/')[0];
1✔
153
  const repo = ghrepo.split('/')[1];
1✔
154
  const newOwner = authenticatedUserInfo.login;
1✔
155
  const date = new Date();
1✔
156
  const newBranch = config.git.branchPrefix + `${date.getFullYear()}${date.getMonth()}${date.getDate()}${date.getHours()}${date.getMinutes()}${date.getSeconds()}`;
1✔
157
  const commitMessage = title;
1✔
158
  const path = source;
1✔
159
  const content = changedDocument;
1✔
160
  const change = {
1✔
161
    path,
162
    content
163
  };
164
  const body = `${desc}` + config.git.commitMessageSuffix;
1✔
165
  // get the token from the local storage
166
  const token = localStorage.getItem('token');
1✔
167
  // make a post request to  /api/github/integration
168
  const response = await fetch(config.server.api.baseUrl + config.server.api.endpoint.integration, {
1✔
169
    method: 'POST',
170
    headers: {
171
      'Content-Type': 'application/json',
172
      'Authorization': `Bearer ${token}`
173
    },
174
    body: JSON.stringify({
175
      owner,
176
      repo,
177
      newOwner,
178
      branch,
179
      newBranch,
180
      commitMessage,
181
      change,
182
      title,
183
      body
184
    })
185
  });
186

187
  const json = await response.json();
1✔
188
  if(!json.url) {
1!
NEW
189
    throw new Error("Unable to open a new PR.")
×
190
  }
191
  return json.url;
1✔
192
};
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