update(ref): clear and typed code

This commit is contained in:
rooyca 2024-06-16 14:31:37 -05:00
parent c25996d2a1
commit da1b264d69
7 changed files with 495 additions and 484 deletions

View file

@ -40,7 +40,7 @@ Este plugin se puede instalar desde Obsidian.
- [ ] Consultas inline de la respuesta de una solicitud
- [ ] Solicitudes predefinidas
- [ ] GUI para bloques de código
- [ ] Traducir la documentación
- [x] Traducir la documentación
## ❤️ Patrocinadores

View file

@ -41,7 +41,7 @@ The plugin can be installed from within Obsidian.
- [ ] Inline query from response
- [ ] Predefined requests
- [ ] GUI for code-blocks
- [ ] Translate documentation
- [x] Translate documentation
## ❤️ Sponsors

View file

@ -6,21 +6,20 @@ The `codeblock` is a versatile block that can be used to write code in different
Flags are the way to specify the parameters of our request and also the format in which we want our response.
| Flag | Default|
| ---| ---------|
| url | |
| method | GET |
| body | |
| headers | |
| show | ALL |
| format | `{}` |
| res-type | json |
| req-id | req-general |
| disabled | |
| req-repeat | 1t@1s |
| notify-if | |
| save-to | |
| properties | |
| Flag | Default |
| ------------| ---------|
| [url](#url) | |
| [method](#method) | GET |
| [body](#body) | |
| [headers](#headers) | |
| [show](#show) | ALL |
| [format](#format) | `{}` |
| [req-id](#req-id) | req-general |
| [disabled](#disabled) | |
| [req-repeat](#req-repeat) | 1t@1s |
| [notify-if](#notify-if) | |
| [save-to](#save-to) | |
| [properties](#properties) | |
### url
@ -140,24 +139,6 @@ format: <h1>{}</h1> <p>{}</p>
!!! note "In this example, first `{}` will be replaced by the title, and second `{}` will be replaced by the body."
### res-type
Specifies the type of response we are getting. The default value is `json`. The available values are:
- json
- other
When the response type is `other`, the response will be displayed as is. If the response is markdown, it will be rendered as markdown, if it's a raw text, it will be displayed as a text, and so on.
!!! danger "Be carefull with `html` responses, it can break the page."
~~~markdown
```req
url: https://raw.githubusercontent.com/Rooyca/Rooyca/main/README.md
res-type: other
```
~~~
### req-id
Specifies the id of the request. The default value is `req-general`. This is useful when we want to store the response in `localStorage` and use it in other blocks or notes.

View file

@ -7,19 +7,19 @@ El `bloque de código` es un bloque versátil que se puede usar para escribir c
Las banderas son la forma de especificar los parámetros de nuestra solicitud y también el formato en el que queremos nuestra respuesta.
| Bandera | Valor predeterminado |
| --- | --------- |
| url | |
| method | GET |
| body | |
| headers | |
| show | ALL |
| format | `{}` |
| res-type | json |
| req-id | req-general |
| disabled | |
| req-repeat | 1t@1s |
| notify-if | |
| save-to | |
| ------------| ---------|
| [url](#url) | |
| [method](#method) | GET |
| [body](#body) | |
| [headers](#headers) | |
| [show](#show) | ALL |
| [format](#format) | `{}` |
| [req-id](#req-id) | req-general |
| [disabled](#disabled) | |
| [req-repeat](#req-repeat) | 1t@1s |
| [notify-if](#notify-if) | |
| [save-to](#save-to) | |
| [properties](#properties) | |
### url
@ -138,25 +138,6 @@ format: <h1>{}</h1> <p>{}</p>
!!! note "En este ejemplo, primero `{}` será reemplazado por el título, y segundo `{}` será reemplazado por el cuerpo."
### res-type
Especifica el tipo de respuesta que estamos obteniendo. El valor predeterminado es `json`. Los valores disponibles son:
- json
- other
Cuando el tipo de respuesta es `other`, la respuesta se mostrará tal como **está**; si es markdown, se renderizará como markdown, si es un texto, se mostrará como texto, y así sucesivamente.
!!! danger "Ten cuidado con las respuestas `html`, pueden romper la página."
~~~markdown
```req
url: https://raw.githubusercontent.com/Rooyca/Rooyca/main/README.md
res-type: other
```
~~~
### req-id
Especifica el ID con la que se almacenará la solicitud. El valor predeterminado es `req-general`. Esto es útil cuando queremos almacenar la respuesta en `localStorage` y usarla en otros bloques o notas.

View file

@ -1,51 +1,53 @@
import { Notice, requestUrl, Editor } from 'obsidian';
// Saves the response to the localStorage
export function saveToID(reqID: any, reqText: any) {
export function saveToID(reqID: string, reqText: string) {
localStorage.setItem(reqID, reqText);
}
// Adds the copy button to the code block
// Take from: https://github.com/jdbrice/obsidian-code-block-copy/
export function addBtnCopy(el: any, copyThis: string) {
const btnCopy = el.createEl("button", {cls: "copy-req", text: "copy"});
export function addBtnCopy(el: HTMLElement, copyThis: string) {
const btnCopy = el.createEl("button", { cls: "copy-req", text: "copy" });
btnCopy.addEventListener('click', function () {
navigator.clipboard.writeText(copyThis).then(function () {
btnCopy.blur();
btnCopy.innerText = 'copied!';
setTimeout(function () {
btnCopy.innerText = 'copy';
}, 2000);
}, function (error) {
btnCopy.innerText = 'Error';
});
navigator.clipboard.writeText(copyThis).then(function () {
btnCopy.blur();
btnCopy.innerText = 'copied!';
setTimeout(function () {
btnCopy.innerText = 'copy';
}, 2000);
}, function () {
btnCopy.innerText = 'Error';
});
});
}
// When more than one {} is defined in "format"
// it will loop through the responses and replace the {} with the respective value
export function replaceOrder(stri, val) {
let index = 0;
let replaced = stri.replace(/{}/g, function(match) {
return val[index++];
});
export function replaceOrder(stri: string, val: []) {
let index = 0;
let replaced = stri.replace(/{}/g, function () {
return val[index++];
});
while (val.length > index) {
if (val[index] === undefined) break;
replaced += "\n"+stri.replace(/{}/g, val[index++]);
}
while (val.length > index) {
if (val[index] === undefined) break;
replaced += "\n" + stri.replace(/{}/g, val[index++]);
}
return replaced;
return replaced;
}
// Check if the user wants a nested response
// In other words, if "->" is present in "show"
export function nestedValue(data: any, key: string) {
export function nestedValue(data, key: string) {
const keySplit: string[] = key.split("->").map((item) => item.trim());
var value: any = "";
for (let i: number = 0; i < keySplit.length; i++) {
let value = "";
for (let i = 0; i < keySplit.length; i++) {
if (i === 0) {
value = data.json[keySplit[i]];
} else {
value = value[keySplit[i]];
} else {
value = value[keySplit[i]];
}
}
@ -59,32 +61,31 @@ export function nestedValue(data: any, key: string) {
// Paste the response to the editor
export function toDocument(settings: any, editor: Editor) {
requestUrl({
url: settings.URL,
method: settings.MethodRequest,
body: settings.DataRequest,
url: settings.URL,
method: settings.MethodRequest,
body: settings.DataRequest,
})
.then((data) => {
if (settings.DataResponse !== "") {
const DataResponseArray = settings.DataResponse.split(",");
for (let i = 0; i < DataResponseArray.length; i++) {
const key = DataResponseArray[i].trim();
.then((data) => {
if (settings.DataResponse !== "") {
const DataResponseArray = settings.DataResponse.split(",");
for (let i = 0; i < DataResponseArray.length; i++) {
const key = DataResponseArray[i].trim();
var value = JSON.stringify(data.json[key]);
let value = JSON.stringify(data.json[key]);
if (key.includes("->")) {
value = nestedValue(data, key);
value = JSON.stringify(value);
}
if (key.includes("->")) {
value = nestedValue(data, key);
value = JSON.stringify(value);
}
editor.replaceSelection("```markdown\n" + `${key.split("->").pop()} : ${value}\n` + "```\n\n");
}
} else {
console.log(data.text)
editor.replaceSelection("<code>\n" + `${data.text}\n` + "</code>\n");
}
})
.catch((error: Error) => {
console.error(error);
new Notice("Error: " + error.message);
});
editor.replaceSelection("```markdown\n" + `${key.split("->").pop()} : ${value}\n` + "```\n\n");
}
} else {
editor.replaceSelection("<code>\n" + `${data.text}\n` + "</code>\n");
}
})
.catch((error: Error) => {
console.error(error);
new Notice("Error: " + error.message);
});
}

View file

@ -1,7 +1,7 @@
import { App, Editor, MarkdownView, Modal, Plugin, PluginSettingTab, Setting, Notice } from 'obsidian';
import { App, Editor, MarkdownView, Modal, Plugin, PluginSettingTab, Setting, Notice, requestUrl } from 'obsidian';
import { readFrontmatter, parseFrontmatter } from './frontmatterUtils';
import { MarkdownParser } from './mdparse';
import { checkFrontmatter, saveToID, addBtnCopy, replaceOrder, nestedValue, toDocument } from './functions';
import { saveToID, addBtnCopy, replaceOrder, nestedValue, toDocument } from './functions';
import { num_braces_regx, num_hyphen_regx, nums_rex, in_braces_regx, varname_regx, no_varname_regx } from './regx';
const parser = new MarkdownParser();
@ -12,8 +12,9 @@ interface LoadAPIRSettings {
DataRequest: string;
HeaderRequest: string;
DataResponse: string;
URLs: string[];
URLs: object[];
Name: string;
DisabledReq: string;
}
const DEFAULT_SETTINGS: LoadAPIRSettings = {
@ -24,6 +25,7 @@ const DEFAULT_SETTINGS: LoadAPIRSettings = {
DataResponse: '',
URLs: [],
Name: '',
DisabledReq: 'This request is disabled',
}
// Checks if the frontmatter is present in the request property
@ -52,10 +54,10 @@ export function checkFrontmatter(req_prop: string) {
console.error(e.message);
new Notice("Error: " + e.message);
return;
}
}
}
return req_prop;
}
return req_prop;
}
export default class MainAPIR extends Plugin {
@ -74,238 +76,270 @@ export default class MainAPIR extends Plugin {
});
try {
this.registerMarkdownCodeBlockProcessor("req", async (source, el, ctx) => {
const sourceLines = source.split("\n");
let method = "GET", allowedMethods = ["GET", "POST", "PUT", "DELETE"], URL = "", show = "",
headers = {}, body = {}, format = "{}", responseType = "json", responseAllow = ["json", "other"], reqID = "req-general",
reqRepeat = { "times": 1, "every": 1000 }, notifyIf = "", saveTo = "", properties = "";
this.registerMarkdownCodeBlockProcessor("req", async (source, el, ctx) => {
const sourceLines = source.split("\n");
let [URL, show, saveTo, reqID] = [String(), String(), String(), String()];
let [notifyIf, properties] = [[String()], [String()]];
// 'format' is not and empty objet, is a string that will be replaced by the response
let [method, format] = ["GET", "{}"];
let [headers, body, reqRepeat] = [Object(), Object(), { "times": 1, "every": 1000 }];
const allowedMethods = ["GET", "POST", "PUT", "DELETE"];
for (const line of sourceLines) {
const lowercaseLine = line.toLowerCase();
if (lowercaseLine.includes("method:")) {
method = line.replace(/method:/i, "").toUpperCase();
method = method.trim();
if (!allowedMethods.includes(method)) {
el.createEl("strong", { text: `Error: Method ${method} not supported` });
return;
}
} else if (lowercaseLine.includes("notify-if:")) {
notifyIf = line.replace(/notify-if:/i, "");
notifyIf = notifyIf.split(" ");
} else if (lowercaseLine.includes("req-repeat:")) {
let repeat_values = line.replace(/req-repeat:/i, "");
repeat_values = repeat_values.split("@");
// check if there are two values and they are numbers
if (repeat_values.length === 2 && !isNaN(repeat_values[0]) && !isNaN(repeat_values[1])) {
reqRepeat = {"times": parseInt(repeat_values[0]), "every": parseInt(repeat_values[1])*1000};
} else {
el.createEl("strong", { text: "Error: req-repeat format is not valid (use Nt@Ns)" });
for (const line of sourceLines) {
const lowercaseLine = line.toLowerCase();
if (lowercaseLine.includes("method:")) {
method = line.replace(/method:/i, "").toUpperCase();
method = method.trim();
if (!allowedMethods.includes(method)) {
el.createEl("strong", { text: `Error: Method ${method} not supported` });
return;
}
} else if (lowercaseLine.includes("url:")) {
URL = checkFrontmatter(line.replace(/url:/i, ""));
if (!URL.includes("http")) {
URL = "https://" + URL;
}
} else if (lowercaseLine.includes("res-type:")) {
responseType = line.replace(/res-type:/i, "").toLowerCase();
responseType = responseType.trim();
if (!responseAllow.includes(responseType)) {
el.createEl("strong", { text: `Error: Response type ${responseType} not supported` });
return;
}
} else if (lowercaseLine.includes("show:")) {
show = line.replace(/show:/i, "");
} else if (lowercaseLine.includes("headers:")) {
headers = JSON.parse(checkFrontmatter(line.replace(/headers:/i, "")));
} else if (lowercaseLine.includes("body:")) {
body = checkFrontmatter(line.replace(/body:/i, ""));
} else if (lowercaseLine.includes("format:")) {
format = line.replace(/format:/i, "");
if (!format.includes("{}")) {
el.createEl("strong", { text: "Error: Use {} to show response in the document." });
return;
}
} else if (lowercaseLine.includes("req-id:")) {
reqID = line.replace(/id:/i, "");
} else if (lowercaseLine.includes("notify-if:")) {
const tempNotifyIf = line.replace(/notify-if:/i, "");
notifyIf = tempNotifyIf.trim().split(" ");
} else if (lowercaseLine.includes("req-repeat:")) {
const repeat_values: string[] = line.replace(/req-repeat:/i, "").trim().split("@");
if (sourceLines.includes("disabled")) {
const idExists = localStorage.getItem(reqID);
if (idExists) {
el.innerHTML += parser.parse(idExists);
return;
} else {
sourceLines.splice(sourceLines.indexOf("disabled"), 1);
}
}
} else if (lowercaseLine.includes("save-to:")) {
saveTo = line.replace(/save-to:/i, "");
if (saveTo === "") {
el.createEl("strong", { text: "Error: save-to value is empty. Please provide a filename" });
return;
}
} else if (lowercaseLine.includes("properties:")) {
properties = line.replace(/properties:/i, "");
properties = properties.replace(/\s/g, "");
properties = properties.split(",");
}
if (URL === "") {
el.createEl("strong", { text: "Error: URL not found" });
return;
}
}
// Function to check if a string is a valid integer
const isValidNumber = (value: string): boolean => {
return /^\d+$/.test(value);
};
if (sourceLines.includes("disabled")) {
el.createEl("strong", { text: "This request is disabled." });
return;
}
// Check if there are two values and they are valid numbers
if (repeat_values.length === 2 && isValidNumber(repeat_values[0]) && isValidNumber(repeat_values[1])) {
reqRepeat = { "times": parseInt(repeat_values[0]), "every": parseInt(repeat_values[1]) * 1000 };
} else {
el.createEl("strong", { text: "Error: req-repeat format is not valid (use T@S)" });
return;
}
for (let i = 0; i < reqRepeat.times; i++) {
try {
const responseData = await requestUrl({ url: URL, method, headers, body });
// Save to File
if (saveTo) {
try {
await this.app.vault.create(saveTo, responseData.text);
new Notice("Saved to " + saveTo);
} catch (e) {
console.error(e.message);
new Notice("Error: " + e.message);
}
}
} else if (lowercaseLine.includes("url:")) {
URL = checkFrontmatter(line.replace(/url:/i, "").trim()) ?? "";
if (!URL) {
el.createEl("strong", { text: "Error: URL not found" });
return;
}
if (URL && !URL.startsWith("http")) {
URL = "https://" + URL;
}
} else if (lowercaseLine.includes("show:")) {
show = line.replace(/show:/i, "").trim();
if (!show) {
el.createEl("strong", { text: "Error: show value is empty" });
return;
}
} else if (lowercaseLine.includes("headers:")) {
const tempHeaders = checkFrontmatter(line.replace(/headers:/i, "")) ?? "";
if (tempHeaders) {
try {
headers = JSON.parse(tempHeaders);
} catch (e) {
el.createEl("strong", { text: "Error: Headers format is not valid" });
return;
}
}
} else if (lowercaseLine.includes("body:")) {
body = checkFrontmatter(line.replace(/body:/i, ""));
} else if (lowercaseLine.includes("format:")) {
format = line.replace(/format:/i, "");
if (!format.includes("{}")) {
el.createEl("strong", { text: "Error: Use {} to show response in the document." });
return;
}
} else if (lowercaseLine.includes("req-id:")) {
reqID = line.replace(/id:/i, "").trim();
if (responseType !== "json") {
try {
el.innerHTML += parser.parse(responseData.text);
} catch (e) {
new Notice("Error: " + e.message);
el.createEl("strong", { text: responseData.text });
}
saveToID(reqID, responseData.text);
addBtnCopy(el, responseData.text);
return;
}
if (sourceLines.includes("disabled")) {
const idExists = localStorage.getItem(reqID);
if (idExists) {
el.createDiv({ text: parser.parse(idExists) });
return;
} else {
sourceLines.splice(sourceLines.indexOf("disabled"), 1);
}
}
} else if (lowercaseLine.includes("save-to:")) {
saveTo = line.replace(/save-to:/i, "").trim();
if (!saveTo) {
el.createEl("strong", { text: "Error: save-to value is empty. Please provide a filename with extension" });
return;
}
} else if (lowercaseLine.includes("properties:")) {
// remove all spaces and split by comma
properties = line.replace(/properties:/i, "").replace(/\s/g, "").split(",");
}
}
if (notifyIf) {
const jsonPath = notifyIf[0];
const symbol = notifyIf[1];
const value = notifyIf[2]
const int_value = parseInt(value);
if (sourceLines.includes("disabled")) {
el.createEl("strong", { text: this.settings.DisabledReq });
return;
}
const jsonPathValue = jsonPath.split(".").reduce((acc, cv) => acc[cv], responseData.json);
const lastValue = jsonPath.split(".").pop();
if (symbol === ">" && jsonPathValue > int_value) {
new Notice("APIR: " + lastValue + " is greater than " + int_value);
} else if (symbol === "<" && jsonPathValue < int_value) {
new Notice("APIR: " + lastValue + " is less than " + int_value);
} else if (symbol === "=" && jsonPathValue === value) {
new Notice("APIR: " + lastValue + " is equal to " + value);
} else if (symbol === ">=" && jsonPathValue >= int_value) {
new Notice("APIR: " + lastValue + " is greater than or equal to " + int_value);
} else if (symbol === "<=" && jsonPathValue <= int_value) {
new Notice("APIR: " + lastValue + " is less than or equal to " + int_value);
}
}
for (let i = 0; i < reqRepeat.times; i++) {
try {
const responseData = await requestUrl({ url: URL, method, headers, body });
// Save to a file
if (saveTo) {
try {
await this.app.vault.create(saveTo, responseData.text);
new Notice("Saved to: " + saveTo);
} catch (e) {
console.error(e.message);
new Notice("Error: " + e.message);
}
}
// Check if the response is not JSON
if (!responseData.headers["content-type"].includes("application/json")) {
try {
el.innerHTML = parser.parse(responseData.text);
} catch (e) {
new Notice("Error: " + e.message);
el.innerHTML = "<pre>" + responseData.text + "</pre>";
}
if (reqID) saveToID(reqID, responseData.text);
addBtnCopy(el, responseData.text);
return;
}
if (notifyIf) {
const jsonPath = notifyIf[0];
const symbol = notifyIf[1];
const value = notifyIf[2]
const int_value = parseInt(value);
const jsonPathValue = jsonPath.split(".").reduce((acc, cv) => acc[cv], responseData.json);
const lastValue = jsonPath.split(".").pop();
if (symbol === ">" && jsonPathValue > int_value) {
new Notice("APIR: " + lastValue + " is greater than " + int_value);
} else if (symbol === "<" && jsonPathValue < int_value) {
new Notice("APIR: " + lastValue + " is less than " + int_value);
} else if (symbol === "=" && jsonPathValue === value) {
new Notice("APIR: " + lastValue + " is equal to " + value);
} else if (symbol === ">=" && jsonPathValue >= int_value) {
new Notice("APIR: " + lastValue + " is greater than or equal to " + int_value);
} else if (symbol === "<=" && jsonPathValue <= int_value) {
new Notice("APIR: " + lastValue + " is less than or equal to " + int_value);
}
}
if (!show) {
el.innerHTML = "<pre>" + JSON.stringify(responseData.json, null, 2) + "</pre>";
saveToID(reqID, el.innerText);
addBtnCopy(el, el.innerText);
} else {
if (show.match(in_braces_regx)) {
if (properties.length > 0 && properties[0] !== '') {
el.createEl("strong", { text: "Error: Properties are not allowed without SHOW" });
return;
}
el.innerHTML = "<pre>" + JSON.stringify(responseData.json, null, 2) + "</pre>";
if (reqID) saveToID(reqID, el.innerText);
addBtnCopy(el, el.innerText);
} else {
const checkBracesRegex = show.match(in_braces_regx);
if (checkBracesRegex) {
if (show.includes(",")) {
el.createEl("strong", { text: "Error: comma is not allowed when using {}" });
return;
}
let temp_show = "";
let range = Object();
if (show.match(num_braces_regx)) {
const range = show.match(nums_rex).map(Number);
try {
range = show.match(nums_rex)!.map(Number)
if (range[0] > range[1]) {
el.createEl("strong", { text: "Error: range is not valid" });
return;
}
for (let i = range[0]; i <= range[1]; i++) {
temp_show += show.replace(show.match(num_braces_regx)[0], i) + ", ";
} catch (e) {
console.error(e.message)
}
const numberBracesRegex = show.match(num_braces_regx)
if (numberBracesRegex) {
for (let i: number = range[0]; i <= range[1]; i++) {
temp_show += show.replace(numberBracesRegex![0], i.toString()) + ", ";
}
show = temp_show;
} else if (show.match(num_hyphen_regx)) {
const numbers = show.match(nums_rex).map(Number);
show = show.replace(in_braces_regx, "-");
for (let i = 0; i < numbers.length; i++) {
temp_show += show.replace("-", numbers[i]) + ", ";
}
show = temp_show;
show = show.replace(in_braces_regx, "-");
for (let i = 0; i < range.length; i++) {
temp_show += show.replace("-", range[i].toString()) + ", ";
}
show = temp_show;
} else {
for (let i = 0; i < responseData.json.length; i++) {
temp_show += show.replace("{..}", i) + ", ";
}
show = temp_show;
}
for (let i = 0; i < responseData.json.length; i++) {
temp_show += show.replace("{..}", i.toString()) + ", ";
}
show = temp_show;
}
}
// adding properties to frontmatter
if (properties) {
if (properties.length > 0 && properties[0] !== '') {
const showArray = show.split(",");
const propertiesArray = properties;
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
const file = activeView.file;
const file: TFile = activeView!.file;
showArray.forEach(async (key, index) => {
await Promise.all(showArray.map(async (key, index) => {
const trimmedKey = key.trim();
let val = "";
if (key.includes("->")) {
val = nestedValue(responseData, key);
} else if (responseData.json && responseData.json[key.trim()]) {
val = responseData.json[key.trim()];
if (trimmedKey.includes("->")) {
val = nestedValue(responseData, trimmedKey);
} else if (responseData.json && responseData.json[trimmedKey]) {
val = responseData.json[trimmedKey];
}
const propertyName = propertiesArray[index].trim();
if (propertyName) {
await this.app.fileManager.processFrontMatter(file, (existingFrontmatter) => {
existingFrontmatter[propertyName] = val;
existingFrontmatter[propertyName] = val;
});
}
});
}));
}
const values = show.includes(",") ? show.split(",").map(key => {
let value = responseData.json[key.trim()];
if (key.includes("->")) value = nestedValue(responseData, key);
return value;
}) : [show.trim().includes("->") ? nestedValue(responseData, show.trim()) : responseData.json[show.trim()]];
const replacedText = replaceOrder(format, values);
el.innerHTML = parser.parse(replacedText);
saveToID(reqID, replacedText);
addBtnCopy(el, replacedText);
}
} catch (error) {
console.error(error);
el.createEl("strong", { text: "Error: " + error.message });
new Notice("Error: " + error.message);
}
await sleep(reqRepeat.every);
}
return;
});
const trimAndProcessKey = (key: string) => {
const trimmedKey = key.trim();
return trimmedKey.includes("->")
? nestedValue(responseData, trimmedKey)
: JSON.stringify(responseData.json[trimmedKey]);
};
const values = show.split(",").map(trimAndProcessKey);
const replacedText = replaceOrder(format, values);
el.innerHTML = parser.parse(replacedText);
if (reqID) saveToID(reqID, replacedText);
addBtnCopy(el, replacedText);
}
} catch (error) {
console.error(error);
el.createEl("strong", { text: "Error: " + error.message });
new Notice("Error: " + error.message);
}
await sleep(reqRepeat.every);
}
return;
});
} catch (e) {
console.error(e.message);
el.createEl("strong", { text: "Error: " + error.message });
new Notice("Error: " + e.message);
console.error(e.message);
new Notice("Error: " + e.message);
}
this.addCommand({
id: 'response-in-document',
name: 'Paste response in current document',
editorCallback: (editor: Editor, view: MarkdownView) => {
editorCallback: (editor: Editor) => {
toDocument(this.settings, editor);
}
});
for (let i = 0; i < this.settings.URLs.length; i++) {
this.addCommand({
id: 'response-in-document-' + this.settings.URLs[i].Name,
name: 'Response for api: ' + this.settings.URLs[i].Name,
id: 'response-in-document-' + this.settings.URLs[i]["Name"],
name: 'Response for api: ' + this.settings.URLs[i]["Name"],
editorCallback: (editor: Editor, view: MarkdownView) => {
const rea = this.settings.URLs[i];
toDocument(rea, editor);
@ -318,12 +352,12 @@ export default class MainAPIR extends Plugin {
onunload() {
console.log('unloading APIR');
// Clean up localStorage
Object.keys(localStorage).forEach(key => {
if (key.startsWith("req-")) {
localStorage.removeItem(key);
}
});
// Clean up localStorage
// Object.keys(localStorage).forEach(key => {
// if (key.startsWith("req-")) {
// localStorage.removeItem(key);
// }
// });
}
async loadSettings() {
@ -336,55 +370,54 @@ export default class MainAPIR extends Plugin {
}
class ShowOutputModal extends Modal {
constructor(app: App, URL: string, MethodRequest: string, DataRequest: string, HeaderRequest: string, DataResponse: string) {
super(app);
this.props = {
URL,
MethodRequest,
DataRequest,
HeaderRequest,
DataResponse,
};
}
onOpen() {
const { contentEl } = this;
const { URL, MethodRequest, DataRequest, HeaderRequest, DataResponse } = this.props;
const handleError = (error) => {
console.error(error);
new Notice("Error: " + error.message);
};
const parseAndCreate = (data) => (key) => {
const json = data.json;
const value = DataResponse.includes("->") ? nestedValue(data, key) : json[key];
contentEl.createEl('b', { text: key + " : " + JSON.stringify(value, null, 2) });
};
const requestOptions = {
url: URL,
method: MethodRequest,
headers: JSON.parse(HeaderRequest),
...(MethodRequest !== "GET" && { body: DataRequest })
};
requestUrl(requestOptions)
.then((data) => {
if (DataResponse !== "") {
const DataResponseArray = DataResponse.split(",");
DataResponseArray.forEach(parseAndCreate(data));
} else {
contentEl.createEl('b', { text: JSON.stringify(data.json, null, 2) });
}
})
.catch(handleError);
constructor(app: App, URL: string, MethodRequest: string, DataRequest: string, HeaderRequest: string, DataResponse: string) {
super(app);
this.props = {
URL,
MethodRequest,
DataRequest,
HeaderRequest,
DataResponse,
};
}
onClose() {
const { contentEl } = this;
contentEl.empty();
}
onOpen() {
const { contentEl } = this;
const { URL, MethodRequest, DataRequest, HeaderRequest, DataResponse } = this.props;
const handleError = (error: Error) => {
console.error(error);
new Notice("Error: " + error.message);
};
const parseAndCreate = (data: object) => (key: string) => {
const value = DataResponse.includes("->") ? nestedValue(data, key) : data[key];
contentEl.createEl('b', { text: key + " : " + JSON.stringify(value, null, 2) });
};
const requestOptions = {
url: URL,
method: MethodRequest,
headers: JSON.parse(HeaderRequest),
...(MethodRequest !== "GET" && { body: DataRequest })
};
requestUrl(requestOptions)
.then((data) => {
if (DataResponse !== "") {
const DataResponseArray = DataResponse.split(",");
DataResponseArray.forEach(parseAndCreate(data));
} else {
contentEl.createEl('b', { text: JSON.stringify(data.json, null, 2) });
}
})
.catch(handleError);
}
onClose() {
const { contentEl } = this;
contentEl.empty();
}
}
class APRSettings extends PluginSettingTab {
@ -396,10 +429,10 @@ class APRSettings extends PluginSettingTab {
}
display(): void {
const {containerEl} = this;
const { containerEl } = this;
containerEl.empty();
containerEl.createEl('h2', { text: "Add Request" });
containerEl.createEl('h2', { text: "Add New Request" });
new Setting(containerEl)
.setName('Name')
@ -424,90 +457,104 @@ class APRSettings extends PluginSettingTab {
this.plugin.settings.URL = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Method')
.setDesc("Select the desired method")
.addDropdown(dropDown => {
dropDown.addOption("GET", "GET");
dropDown.addOption("POST", "POST");
dropDown.addOption("POST", "PUT");
dropDown.addOption("DELETE", "DELETE");
dropDown.setValue(this.plugin.settings.MethodRequest)
dropDown.onChange(async value => {
this.plugin.settings.MethodRequest = value;
await this.plugin.saveSettings();
});
});
new Setting(containerEl)
.setName('Body')
.setDesc("Data to send in the body")
.addTextArea(text => text
.setPlaceholder('{"data":"data"}')
.setValue(this.plugin.settings.DataRequest)
.onChange(async (value) => {
this.plugin.settings.DataRequest = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Headers')
.setDesc("The headers of the request")
.addTextArea(text => text
.setPlaceholder('{"Content-Type": "application/json"}')
.setValue(this.plugin.settings.HeaderRequest)
.onChange(async (value) => {
this.plugin.settings.HeaderRequest = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('What to display')
.setDesc("Write the name of the variables you want to show (spaced by comma)")
.addTextArea(text => text
.setPlaceholder('varname')
.setValue(this.plugin.settings.DataResponse)
.onChange(async (value) => {
this.plugin.settings.DataResponse = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.addButton(button => {
button.setClass('btn-add-apir');
button.setButtonText('ADD').onClick(async () => {
const {Name} = this.plugin.settings;
if (Name === "") {
new Notice("Name is empty");
return;
}
const {URL, MethodRequest, DataResponse, DataRequest, HeaderRequest} = this.plugin.settings;
const {URLs} = this.plugin.settings;
URLs.push({
'URL': URL,
'Name': Name,
'MethodRequest': MethodRequest,
'DataRequest': DataRequest,
'HeaderRequest': HeaderRequest,
'DataResponse': DataResponse
});
await this.plugin.saveSettings();
this.display();
this.plugin.addCommand({
id: 'response-in-document-' + Name,
name: 'Response for api: ' + Name,
editorCallback: (editor: Editor, view: MarkdownView) => {
const rea = URLs[URLs.length - 1];
toDocument(rea, editor);
}
});
});
});
new Setting(containerEl)
.setName('Method')
.setDesc("Select the desired method")
.addDropdown(dropDown => {
dropDown.addOption("GET", "GET");
dropDown.addOption("POST", "POST");
dropDown.addOption("POST", "PUT");
dropDown.addOption("DELETE", "DELETE");
dropDown.setValue(this.plugin.settings.MethodRequest)
dropDown.onChange(async value => {
this.plugin.settings.MethodRequest = value;
await this.plugin.saveSettings();
});
});
new Setting(containerEl)
.setName('Body')
.setDesc("Data to send in the body")
.addTextArea(text => text
.setPlaceholder('{"data":"data"}')
.setValue(this.plugin.settings.DataRequest)
.onChange(async (value) => {
this.plugin.settings.DataRequest = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Headers')
.setDesc("The headers of the request")
.addTextArea(text => text
.setPlaceholder('{"Content-Type": "application/json"}')
.setValue(this.plugin.settings.HeaderRequest)
.onChange(async (value) => {
this.plugin.settings.HeaderRequest = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('What to display')
.setDesc("Write the name of the variables you want to show (spaced by comma)")
.addText(text => text
.setPlaceholder('varname')
.setValue(this.plugin.settings.DataResponse)
.onChange(async (value) => {
this.plugin.settings.DataResponse = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.addButton(button => {
button.setClass('btn-add-apir');
button.setButtonText('ADD').onClick(async () => {
const { Name } = this.plugin.settings;
if (Name === "") {
new Notice("Name is empty");
return;
}
const { URL, MethodRequest, DataResponse, DataRequest, HeaderRequest } = this.plugin.settings;
const { URLs } = this.plugin.settings;
URLs.push({
'URL': URL,
'Name': Name,
'MethodRequest': MethodRequest,
'DataRequest': DataRequest,
'HeaderRequest': HeaderRequest,
'DataResponse': DataResponse
});
await this.plugin.saveSettings();
this.display();
this.plugin.addCommand({
id: 'response-in-document-' + Name,
name: 'Response for api: ' + Name,
editorCallback: (editor: Editor) => {
const rea = URLs[URLs.length - 1];
toDocument(rea, editor);
}
});
});
});
containerEl.createEl('hr');
containerEl.createEl('hr');
containerEl.createEl('h2', { text: 'Codeblock Settings' });
new Setting(containerEl)
.setName('Text when request is Disabled')
.setDesc("What to show when a request is disabled")
.addText(text => text
.setPlaceholder('This request is disabled')
.setValue(this.plugin.settings.DisabledReq)
.onChange(async (value) => {
this.plugin.settings.DisabledReq = value;
await this.plugin.saveSettings();
}));
containerEl.createEl('hr');
containerEl.createEl('h2', { text: 'Manage Requests' });
this.displayInfoApirs(containerEl);
new Setting(containerEl)
.addButton(button => {
button.setClass('btn-clear-apir');
new Setting(containerEl)
.addButton(button => {
button.setClass('btn-clear-apir');
button.setButtonText("Clear localStorage").onClick(async () => {
Object.keys(localStorage).forEach(key => {
if (key.startsWith("req-")) {
@ -516,62 +563,62 @@ class APRSettings extends PluginSettingTab {
});
this.display();
});
});
}
});
}
displayInfoApirs(containerEl: HTMLElement) {
displayInfoApirs(containerEl: HTMLElement) {
// Render URL table
const { URLs } = this.plugin.settings;
const ct = containerEl.createEl('div', { cls: 'cocontainer' });
if (URLs.length > 0) {
const tableContainer = ct.createEl('div', { cls: 'table-container' });
const { URLs } = this.plugin.settings;
const ct = containerEl.createEl('div', { cls: 'cocontainer' });
if (URLs.length > 0) {
const tableContainer = ct.createEl('div', { cls: 'table-container' });
const table = tableContainer.createEl('table', { cls: 'api-table' });
const thead = table.createEl('thead');
const headerRow = thead.createEl('tr');
headerRow.createEl('th', { text: 'Name' });
headerRow.createEl('th', { text: 'URL' });
const table = tableContainer.createEl('table', { cls: 'api-table' });
const thead = table.createEl('thead');
const headerRow = thead.createEl('tr');
headerRow.createEl('th', { text: 'Name' });
headerRow.createEl('th', { text: 'URL' });
const tbody = table.createEl('tbody');
URLs.forEach((url) => {
const row = tbody.createEl('tr');
const tbody = table.createEl('tbody');
URLs.forEach((url) => {
const row = tbody.createEl('tr');
row.createEl('td', { text: url.Name });
row.createEl('td', { text: url.Name });
const urlCell = row.createEl('td');
urlCell.createEl('a', { text: url.URL.length > 50 ? url.URL.substring(0, 50) + "..." : url.URL, cls: 'api-url' });
const urlCell = row.createEl('td');
urlCell.createEl('a', { text: url.URL.length > 50 ? url.URL.substring(0, 50) + "..." : url.URL, cls: 'api-url' });
urlCell.addEventListener('click', async () => {
const index = URLs.indexOf(url);
URLs.splice(index, 1);
await this.plugin.saveSettings();
this.display();
});
});
}
urlCell.addEventListener('click', async () => {
const index = URLs.indexOf(url);
URLs.splice(index, 1);
await this.plugin.saveSettings();
this.display();
});
});
}
// Render localStorage table
const tableContainer = containerEl.createEl('div', { cls: 'table-container full-width' });
const table = ct.createEl('table', { cls: 'api-table full-width' });
const thead = table.createEl('thead');
const headerRow = thead.createEl('tr');
let hr = headerRow.createEl('th', { text: 'ID' });
// Render localStorage table
containerEl.createEl('div', { cls: 'table-container full-width' });
const table = ct.createEl('table', { cls: 'api-table full-width' });
const thead = table.createEl('thead');
const headerRow = thead.createEl('tr');
const hr = headerRow.createEl('th', { text: 'ID' });
const tbody = table.createEl('tbody');
Object.keys(localStorage).forEach(key => {
if (key.startsWith("req-")) {
const row = tbody.createEl('tr');
const idCell = row.createEl('td', { text: key });
const tbody = table.createEl('tbody');
Object.keys(localStorage).forEach(key => {
if (key.startsWith("req-")) {
const row = tbody.createEl('tr');
const idCell = row.createEl('td', { text: key });
idCell.addEventListener('click', async () => {
localStorage.removeItem(key);
this.display();
});
}
});
// if table is empty
if (tbody.children.length === 0) {
hr.innerText = 'No response saved';
idCell.addEventListener('click', async () => {
localStorage.removeItem(key);
this.display();
});
}
});
// if table is empty
if (tbody.children.length === 0) {
hr.innerText = 'No response saved';
}
}

View file

@ -2,6 +2,9 @@
export class MarkdownParser {
parse(text) {
// Remove all <script> tags
text = text.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
// Headers
text = text.replace(/^(#{1,6})\s*(.*)$/gm, (match, p1, p2) => `<h${p1.length}>${p2}</h${p1.length}>`);
@ -24,7 +27,7 @@ export class MarkdownParser {
text = text.replace(/\*(.*?)\*/g, '<em>$1</em>');
// Images
text = text.replace(/\!\[([^\]]+)\]\(([^)]+)\)/g, '<img src="$2" alt="$1">');
text = text.replace(/!\[([^\]]+)\]\(([^)]+)\)/g, '<img src="$2" alt="$1">');
// Links
text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
@ -57,8 +60,6 @@ export class MarkdownParser {
let bodyEnd = '</tbody>\n';
let rowStart = '<tr>\n';
let rowEnd = '</tr>\n';
let cellStart = '<td>';
let cellEnd = '</td>\n';
let headerRow = rowStart + headerCells.map(cell => `<th>${cell}</th>`).join('\n') + rowEnd;
let bodyRows = rows.map(row => {
let cells = row.split('|').map(cell => cell.trim()).filter(cell => cell);