Merge branch 'master' into update-proxy-info

This commit is contained in:
John Harker
2020-02-26 23:56:39 +00:00
committed by GitHub
87 changed files with 7368 additions and 3777 deletions

7
.prettierignore Normal file
View File

@@ -0,0 +1,7 @@
.dependabot
.github
.nuxt
.postwoman
.vscode
package-lock.json
node_modules

View File

@@ -1,6 +1,6 @@
import * as cookie from "cookie";
import * as URL from "url";
import * as querystring from "querystring";
import * as cookie from "cookie"
import * as URL from "url"
import * as querystring from "querystring"
/**
* given this: [ 'msg1=value1', 'msg2=value2' ]
@@ -8,121 +8,118 @@ import * as querystring from "querystring";
* @param dataArguments
*/
const joinDataArguments = dataArguments => {
let data = "";
let data = ""
dataArguments.forEach((argument, i) => {
if (i === 0) {
data += argument;
data += argument
} else {
data += `&${argument}`;
data += `&${argument}`
}
});
return data;
};
})
return data
}
const parseCurlCommand = curlCommand => {
let newlineFound = /\r|\n/.exec(curlCommand);
let newlineFound = /\r|\n/.exec(curlCommand)
if (newlineFound) {
// remove newlines
curlCommand = curlCommand.replace(/\r|\n/g, "");
curlCommand = curlCommand.replace(/\r|\n/g, "")
}
// yargs parses -XPOST as separate arguments. just prescreen for it.
curlCommand = curlCommand.replace(/ -XPOST/, " -X POST");
curlCommand = curlCommand.replace(/ -XGET/, " -X GET");
curlCommand = curlCommand.replace(/ -XPUT/, " -X PUT");
curlCommand = curlCommand.replace(/ -XPATCH/, " -X PATCH");
curlCommand = curlCommand.replace(/ -XDELETE/, " -X DELETE");
curlCommand = curlCommand.trim();
let parsedArguments = require("yargs-parser")(curlCommand);
let cookieString;
let cookies;
let url = parsedArguments._[1];
curlCommand = curlCommand.replace(/ -XPOST/, " -X POST")
curlCommand = curlCommand.replace(/ -XGET/, " -X GET")
curlCommand = curlCommand.replace(/ -XPUT/, " -X PUT")
curlCommand = curlCommand.replace(/ -XPATCH/, " -X PATCH")
curlCommand = curlCommand.replace(/ -XDELETE/, " -X DELETE")
curlCommand = curlCommand.trim()
let parsedArguments = require("yargs-parser")(curlCommand)
let cookieString
let cookies
let url = parsedArguments._[1]
if (!url) {
for (let argName in parsedArguments) {
if (typeof parsedArguments[argName] === "string") {
if (["http", "www."].includes(parsedArguments[argName])) {
url = parsedArguments[argName];
url = parsedArguments[argName]
}
}
}
}
let headers;
let headers
const parseHeaders = headerFieldName => {
if (parsedArguments[headerFieldName]) {
if (!headers) {
headers = {};
headers = {}
}
if (!Array.isArray(parsedArguments[headerFieldName])) {
parsedArguments[headerFieldName] = [parsedArguments[headerFieldName]];
parsedArguments[headerFieldName] = [parsedArguments[headerFieldName]]
}
parsedArguments[headerFieldName].forEach(header => {
if (header.includes("Cookie")) {
// stupid javascript tricks: closure
cookieString = header;
cookieString = header
} else {
let colonIndex = header.indexOf(":");
let headerName = header.substring(0, colonIndex);
let headerValue = header.substring(colonIndex + 1).trim();
headers[headerName] = headerValue;
let colonIndex = header.indexOf(":")
let headerName = header.substring(0, colonIndex)
let headerValue = header.substring(colonIndex + 1).trim()
headers[headerName] = headerValue
}
});
})
}
};
}
parseHeaders("H");
parseHeaders("header");
parseHeaders("H")
parseHeaders("header")
if (parsedArguments.A) {
if (!headers) {
headers = [];
headers = []
}
headers["User-Agent"] = parsedArguments.A;
headers["User-Agent"] = parsedArguments.A
} else if (parsedArguments["user-agent"]) {
if (!headers) {
headers = [];
headers = []
}
headers["User-Agent"] = parsedArguments["user-agent"];
headers["User-Agent"] = parsedArguments["user-agent"]
}
if (parsedArguments.b) {
cookieString = parsedArguments.b;
cookieString = parsedArguments.b
}
if (parsedArguments.cookie) {
cookieString = parsedArguments.cookie;
cookieString = parsedArguments.cookie
}
let multipartUploads;
let multipartUploads
if (parsedArguments.F) {
multipartUploads = {};
multipartUploads = {}
if (!Array.isArray(parsedArguments.F)) {
parsedArguments.F = [parsedArguments.F];
parsedArguments.F = [parsedArguments.F]
}
parsedArguments.F.forEach(multipartArgument => {
// input looks like key=value. value could be json or a file path prepended with an @
const [key, value] = multipartArgument.split("=", 2);
multipartUploads[key] = value;
});
const [key, value] = multipartArgument.split("=", 2)
multipartUploads[key] = value
})
}
if (cookieString) {
const cookieParseOptions = {
decode: s => s
};
decode: s => s,
}
// separate out cookie headers into separate data structure
// note: cookie is case insensitive
cookies = cookie.parse(
cookieString.replace(/^Cookie: /gi, ""),
cookieParseOptions
);
cookies = cookie.parse(cookieString.replace(/^Cookie: /gi, ""), cookieParseOptions)
}
let method;
let method
if (parsedArguments.X === "POST") {
method = "post";
method = "post"
} else if (parsedArguments.X === "PUT" || parsedArguments["T"]) {
method = "put";
method = "put"
} else if (parsedArguments.X === "PATCH") {
method = "patch";
method = "patch"
} else if (parsedArguments.X === "DELETE") {
method = "delete";
method = "delete"
} else if (parsedArguments.X === "OPTIONS") {
method = "options";
method = "options"
} else if (
(parsedArguments["d"] ||
parsedArguments["data"] ||
@@ -132,95 +129,94 @@ const parseCurlCommand = curlCommand => {
parsedArguments["form"]) &&
!(parsedArguments["G"] || parsedArguments["get"])
) {
method = "post";
method = "post"
} else if (parsedArguments["I"] || parsedArguments["head"]) {
method = "head";
method = "head"
} else {
method = "get";
method = "get"
}
let compressed = !!parsedArguments.compressed;
let urlObject = URL.parse(url); // eslint-disable-line
let compressed = !!parsedArguments.compressed
let urlObject = URL.parse(url) // eslint-disable-line
// if GET request with data, convert data to query string
// NB: the -G flag does not change the http verb. It just moves the data into the url.
if (parsedArguments["G"] || parsedArguments["get"]) {
urlObject.query = urlObject.query ? urlObject.query : "";
let option =
"d" in parsedArguments ? "d" : "data" in parsedArguments ? "data" : null;
urlObject.query = urlObject.query ? urlObject.query : ""
let option = "d" in parsedArguments ? "d" : "data" in parsedArguments ? "data" : null
if (option) {
let urlQueryString = "";
let urlQueryString = ""
if (!url.includes("?")) {
url += "?";
url += "?"
} else {
urlQueryString += "&";
urlQueryString += "&"
}
if (typeof parsedArguments[option] === "object") {
urlQueryString += parsedArguments[option].join("&");
urlQueryString += parsedArguments[option].join("&")
} else {
urlQueryString += parsedArguments[option];
urlQueryString += parsedArguments[option]
}
urlObject.query += urlQueryString;
url += urlQueryString;
delete parsedArguments[option];
urlObject.query += urlQueryString
url += urlQueryString
delete parsedArguments[option]
}
}
let query = querystring.parse(urlObject.query, null, null, {
maxKeys: 10000
});
maxKeys: 10000,
})
urlObject.search = null; // Clean out the search/query portion.
urlObject.search = null // Clean out the search/query portion.
const request = {
url,
urlWithoutQuery: URL.format(urlObject)
};
urlWithoutQuery: URL.format(urlObject),
}
if (compressed) {
request["compressed"] = true;
request["compressed"] = true
}
if (Object.keys(query).length > 0) {
request.query = query;
request.query = query
}
if (headers) {
request.headers = headers;
request.headers = headers
}
request["method"] = method;
request["method"] = method
if (cookies) {
request.cookies = cookies;
request.cookieString = cookieString.replace("Cookie: ", "");
request.cookies = cookies
request.cookieString = cookieString.replace("Cookie: ", "")
}
if (multipartUploads) {
request.multipartUploads = multipartUploads;
request.multipartUploads = multipartUploads
}
if (parsedArguments.data) {
request.data = parsedArguments.data;
request.data = parsedArguments.data
} else if (parsedArguments["data-binary"]) {
request.data = parsedArguments["data-binary"];
request.isDataBinary = true;
request.data = parsedArguments["data-binary"]
request.isDataBinary = true
} else if (parsedArguments["d"]) {
request.data = parsedArguments["d"];
request.data = parsedArguments["d"]
} else if (parsedArguments["data-ascii"]) {
request.data = parsedArguments["data-ascii"];
request.data = parsedArguments["data-ascii"]
}
if (parsedArguments["u"]) {
request.auth = parsedArguments["u"];
request.auth = parsedArguments["u"]
}
if (parsedArguments["user"]) {
request.auth = parsedArguments["user"];
request.auth = parsedArguments["user"]
}
if (Array.isArray(request.data)) {
request.dataArray = request.data;
request.data = joinDataArguments(request.data);
request.dataArray = request.data
request.data = joinDataArguments(request.data)
}
if (parsedArguments["k"] || parsedArguments["insecure"]) {
request.insecure = true;
request.insecure = true
}
return request;
};
return request
}
export default parseCurlCommand;
export default parseCurlCommand

View File

@@ -1,4 +1,4 @@
const redirectUri = `${window.location.origin}/`;
const redirectUri = `${window.location.origin}/`
// GENERAL HELPER FUNCTIONS
@@ -13,23 +13,23 @@ const redirectUri = `${window.location.origin}/`;
const sendPostRequest = async (url, params) => {
const body = Object.keys(params)
.map(key => `${key}=${params[key]}`)
.join("&");
.join("&")
const options = {
method: "post",
headers: {
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8",
},
body
};
try {
const response = await fetch(url, options);
const data = await response.json();
return data;
} catch (err) {
console.error("Request failed", err);
throw err;
body,
}
};
try {
const response = await fetch(url, options)
const data = await response.json()
return data
} catch (err) {
console.error("Request failed", err)
throw err
}
}
/**
* Parse a query string into an object
@@ -40,15 +40,12 @@ const sendPostRequest = async (url, params) => {
const parseQueryString = searchQuery => {
if (searchQuery === "") {
return {};
return {}
}
const segments = searchQuery.split("&").map(s => s.split("="));
const queryString = segments.reduce(
(obj, el) => ({ ...obj, [el[0]]: el[1] }),
{}
);
return queryString;
};
const segments = searchQuery.split("&").map(s => s.split("="))
const queryString = segments.reduce((obj, el) => ({ ...obj, [el[0]]: el[1] }), {})
return queryString
}
/**
* Get OAuth configuration from OpenID Discovery endpoint
@@ -60,18 +57,18 @@ const getTokenConfiguration = async endpoint => {
const options = {
method: "GET",
headers: {
"Content-type": "application/json"
}
};
try {
const response = await fetch(endpoint, options);
const config = await response.json();
return config;
} catch (err) {
console.error("Request failed", err);
throw err;
"Content-type": "application/json",
},
}
};
try {
const response = await fetch(endpoint, options)
const config = await response.json()
return config
} catch (err) {
console.error("Request failed", err)
throw err
}
}
// PKCE HELPER FUNCTIONS
@@ -82,10 +79,10 @@ const getTokenConfiguration = async endpoint => {
*/
const generateRandomString = () => {
const array = new Uint32Array(28);
window.crypto.getRandomValues(array);
return Array.from(array, dec => `0${dec.toString(16)}`.substr(-2)).join("");
};
const array = new Uint32Array(28)
window.crypto.getRandomValues(array)
return Array.from(array, dec => `0${dec.toString(16)}`.substr(-2)).join("")
}
/**
* Calculate the SHA256 hash of the input text
@@ -94,10 +91,10 @@ const generateRandomString = () => {
*/
const sha256 = plain => {
const encoder = new TextEncoder();
const data = encoder.encode(plain);
return window.crypto.subtle.digest("SHA-256", data);
};
const encoder = new TextEncoder()
const data = encoder.encode(plain)
return window.crypto.subtle.digest("SHA-256", data)
}
/**
* Encodes the input string into Base64 format
@@ -115,7 +112,7 @@ const base64urlencode = (
btoa(String.fromCharCode.apply(null, new Uint8Array(str)))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
.replace(/=+$/, "")
/**
* Return the base64-urlencoded sha256 hash for the PKCE challenge
@@ -125,9 +122,9 @@ const base64urlencode = (
*/
const pkceChallengeFromVerifier = async v => {
const hashed = await sha256(v);
return base64urlencode(hashed);
};
const hashed = await sha256(v)
return base64urlencode(hashed)
}
// OAUTH REQUEST
@@ -144,32 +141,29 @@ const tokenRequest = async ({
authUrl,
accessTokenUrl,
clientId,
scope
scope,
}) => {
// Check oauth configuration
if (oidcDiscoveryUrl !== "") {
const {
authorization_endpoint,
token_endpoint
} = await getTokenConfiguration(oidcDiscoveryUrl);
authUrl = authorization_endpoint;
accessTokenUrl = token_endpoint;
const { authorization_endpoint, token_endpoint } = await getTokenConfiguration(oidcDiscoveryUrl)
authUrl = authorization_endpoint
accessTokenUrl = token_endpoint
}
// Store oauth information
localStorage.setItem("token_endpoint", accessTokenUrl);
localStorage.setItem("client_id", clientId);
localStorage.setItem("token_endpoint", accessTokenUrl)
localStorage.setItem("client_id", clientId)
// Create and store a random state value
const state = generateRandomString();
localStorage.setItem("pkce_state", state);
const state = generateRandomString()
localStorage.setItem("pkce_state", state)
// Create and store a new PKCE code_verifier (the plaintext random secret)
const code_verifier = generateRandomString();
localStorage.setItem("pkce_code_verifier", code_verifier);
const code_verifier = generateRandomString()
localStorage.setItem("pkce_code_verifier", code_verifier)
// Hash and base64-urlencode the secret to use as the challenge
const code_challenge = await pkceChallengeFromVerifier(code_verifier);
const code_challenge = await pkceChallengeFromVerifier(code_verifier)
// Build the authorization URL
const buildUrl = () =>
@@ -177,15 +171,13 @@ const tokenRequest = async ({
clientId
)}&state=${encodeURIComponent(state)}&scope=${encodeURIComponent(
scope
)}&redirect_uri=${encodeURIComponent(
redirectUri
)}&code_challenge=${encodeURIComponent(
)}&redirect_uri=${encodeURIComponent(redirectUri)}&code_challenge=${encodeURIComponent(
code_challenge
)}&code_challenge_method=S256`;
)}&code_challenge_method=S256`
// Redirect to the authorization server
window.location = buildUrl();
};
window.location = buildUrl()
}
// OAUTH REDIRECT HANDLING
@@ -197,42 +189,39 @@ const tokenRequest = async ({
*/
const oauthRedirect = async () => {
let tokenResponse = "";
let q = parseQueryString(window.location.search.substring(1));
let tokenResponse = ""
let q = parseQueryString(window.location.search.substring(1))
// Check if the server returned an error string
if (q.error) {
alert(`Error returned from authorization server: ${q.error}`);
alert(`Error returned from authorization server: ${q.error}`)
}
// If the server returned an authorization code, attempt to exchange it for an access token
if (q.code) {
// Verify state matches what we set at the beginning
if (localStorage.getItem("pkce_state") != q.state) {
alert("Invalid state");
alert("Invalid state")
} else {
try {
// Exchange the authorization code for an access token
tokenResponse = await sendPostRequest(
localStorage.getItem("token_endpoint"),
{
grant_type: "authorization_code",
code: q.code,
client_id: localStorage.getItem("client_id"),
redirect_uri: redirectUri,
code_verifier: localStorage.getItem("pkce_code_verifier")
}
);
tokenResponse = await sendPostRequest(localStorage.getItem("token_endpoint"), {
grant_type: "authorization_code",
code: q.code,
client_id: localStorage.getItem("client_id"),
redirect_uri: redirectUri,
code_verifier: localStorage.getItem("pkce_code_verifier"),
})
} catch (err) {
console.log(`${error.error}\n\n${error.error_description}`);
console.log(`${error.error}\n\n${error.error_description}`)
}
}
// Clean these up since we don't need them anymore
localStorage.removeItem("pkce_state");
localStorage.removeItem("pkce_code_verifier");
localStorage.removeItem("token_endpoint");
localStorage.removeItem("client_id");
return tokenResponse;
localStorage.removeItem("pkce_state")
localStorage.removeItem("pkce_code_verifier")
localStorage.removeItem("token_endpoint")
localStorage.removeItem("client_id")
return tokenResponse
}
return tokenResponse;
};
return tokenResponse
}
export { tokenRequest, oauthRedirect };
export { tokenRequest, oauthRedirect }

View File

@@ -2,55 +2,50 @@ export default () => {
//*** Determine whether or not the PWA has been installed. ***//
// Step 1: Check local storage
let pwaInstalled = localStorage.getItem("pwaInstalled") === "yes";
let pwaInstalled = localStorage.getItem("pwaInstalled") === "yes"
// Step 2: Check if the display-mode is standalone. (Only permitted for PWAs.)
if (
!pwaInstalled &&
window.matchMedia("(display-mode: standalone)").matches
) {
localStorage.setItem("pwaInstalled", "yes");
pwaInstalled = true;
if (!pwaInstalled && window.matchMedia("(display-mode: standalone)").matches) {
localStorage.setItem("pwaInstalled", "yes")
pwaInstalled = true
}
// Step 3: Check if the navigator is in standalone mode. (Again, only permitted for PWAs.)
if (!pwaInstalled && window.navigator.standalone === true) {
localStorage.setItem("pwaInstalled", "yes");
pwaInstalled = true;
localStorage.setItem("pwaInstalled", "yes")
pwaInstalled = true
}
//*** If the PWA has not been installed, show the install PWA prompt.. ***//
let deferredPrompt = null;
let deferredPrompt = null
window.addEventListener("beforeinstallprompt", event => {
deferredPrompt = event;
deferredPrompt = event
// Show the install button if the prompt appeared.
if (!pwaInstalled) {
document.querySelector("#installPWA").style.display = "inline-flex";
document.querySelector("#installPWA").style.display = "inline-flex"
}
});
})
// When the app is installed, remove install prompts.
window.addEventListener("appinstalled", event => {
localStorage.setItem("pwaInstalled", "yes");
pwaInstalled = true;
document.getElementById("installPWA").style.display = "none";
});
localStorage.setItem("pwaInstalled", "yes")
pwaInstalled = true
document.getElementById("installPWA").style.display = "none"
})
// When the app is uninstalled, add the prompts back
return async () => {
if (deferredPrompt) {
deferredPrompt.prompt();
let outcome = await deferredPrompt.userChoice;
deferredPrompt.prompt()
let outcome = await deferredPrompt.userChoice
if (outcome === "accepted") {
console.log("Postwoman was installed successfully.");
console.log("Postwoman was installed successfully.")
} else {
console.log(
"Postwoman could not be installed. (Installation rejected by user.)"
);
console.log("Postwoman could not be installed. (Installation rejected by user.)")
}
deferredPrompt = null;
deferredPrompt = null
}
};
};
}
}

View File

@@ -1,27 +1,26 @@
const axios = require("axios");
const fs = require("fs");
const { spawnSync } = require("child_process");
const axios = require("axios")
const fs = require("fs")
const { spawnSync } = require("child_process")
const runCommand = (command, args) =>
spawnSync(command, args)
.stdout.toString()
.replace(/\n/g, "");
.replace(/\n/g, "")
const FAIL_ON_ERROR = false;
const PW_BUILD_DATA_DIR = "./.postwoman";
const IS_DEV_MODE = process.argv.includes("--dev");
const FAIL_ON_ERROR = false
const PW_BUILD_DATA_DIR = "./.postwoman"
const IS_DEV_MODE = process.argv.includes("--dev")
try {
(async () => {
;(async () => {
// Create the build data directory if it does not exist.
if (!fs.existsSync(PW_BUILD_DATA_DIR)) {
fs.mkdirSync(PW_BUILD_DATA_DIR);
fs.mkdirSync(PW_BUILD_DATA_DIR)
}
let version = {};
let version = {}
// Get the current version name as the tag from Git.
version.name =
process.env.TRAVIS_TAG ||
runCommand("git", ["tag --sort=committerdate | tail -1"]);
process.env.TRAVIS_TAG || runCommand("git", ["tag --sort=committerdate | tail -1"])
// FALLBACK: If version.name was unset, let's grab it from GitHub.
if (!version.name) {
@@ -32,32 +31,29 @@ try {
.catch(ex => ({
data: [
{
tag_name: require("./package.json").version
}
]
tag_name: require("./package.json").version,
},
],
}))
).data[0]["tag_name"];
).data[0]["tag_name"]
}
// Get the current version hash as the short hash from Git.
version.hash = runCommand("git", ["rev-parse", "--short", "HEAD"]);
version.hash = runCommand("git", ["rev-parse", "--short", "HEAD"])
// Get the 'variant' name as the branch, if it's not master.
version.variant =
process.env.TRAVIS_BRANCH ||
runCommand("git", ["branch"])
.split("* ")[1]
.split(" ")[0] + (IS_DEV_MODE ? " - DEV MODE" : "");
.split(" ")[0] + (IS_DEV_MODE ? " - DEV MODE" : "")
if (["", "master"].includes(version.variant)) {
delete version.variant;
delete version.variant
}
// Write version data into a file
fs.writeFileSync(
`${PW_BUILD_DATA_DIR}/version.json`,
JSON.stringify(version)
);
})();
fs.writeFileSync(`${PW_BUILD_DATA_DIR}/version.json`, JSON.stringify(version))
})()
} catch (ex) {
console.error(ex);
process.exit(FAIL_ON_ERROR ? 1 : 0);
console.error(ex)
process.exit(FAIL_ON_ERROR ? 1 : 0)
}

View File

@@ -5,114 +5,143 @@
</template>
<style lang="scss">
.show-if-initialized {
opacity: 0;
.show-if-initialized {
opacity: 0;
&.initialized {
opacity: 1;
}
& > * {
transition: none;
}
&.initialized {
opacity: 1;
}
& > * {
transition: none;
}
}
</style>
<script>
const DEFAULT_THEME = "twilight";
const DEFAULT_THEME = "twilight"
import ace from "ace-builds";
import "ace-builds/webpack-resolver";
import ace from "ace-builds"
import "ace-builds/webpack-resolver"
import jsonParse from "../functions/jsonParse"
import debounce from "../functions/utils/debounce"
export default {
props: {
value: {
type: String,
default: ""
default: "",
},
theme: {
type: String,
required: false
required: false,
},
lang: {
type: String,
default: "json"
default: "json",
},
lint: {
type: Boolean,
default: true,
required: false,
},
options: {
type: Object,
default: {}
}
default: {},
},
},
data() {
return {
initialized: false,
editor: null,
cacheValue: ""
};
cacheValue: "",
}
},
watch: {
value(value) {
if (value !== this.cacheValue) {
this.editor.session.setValue(value, 1);
this.cacheValue = value;
this.editor.session.setValue(value, 1)
this.cacheValue = value
if (this.lint) this.provideLinting(value)
}
},
theme() {
this.initialized = false;
this.initialized = false
this.editor.setTheme(`ace/theme/${this.defineTheme()}`, () => {
this.$nextTick().then(() => {
this.initialized = true;
});
});
this.initialized = true
})
})
},
lang(value) {
this.editor.getSession().setMode("ace/mode/" + value);
this.editor.getSession().setMode("ace/mode/" + value)
},
options(value) {
this.editor.setOptions(value);
}
this.editor.setOptions(value)
},
},
mounted() {
const editor = ace.edit(this.$refs.editor, {
mode: `ace/mode/${this.lang}`,
...this.options
});
...this.options,
})
// Set the theme and show the editor only after it's been set to prevent FOUC.
editor.setTheme(`ace/theme/${this.defineTheme()}`, () => {
this.$nextTick().then(() => {
this.initialized = true;
});
});
this.initialized = true
})
})
if (this.value) editor.setValue(this.value, 1);
if (this.value) editor.setValue(this.value, 1)
this.editor = editor;
this.cacheValue = this.value;
this.editor = editor
this.cacheValue = this.value
editor.on("change", () => {
const content = editor.getValue();
this.$emit("input", content);
this.cacheValue = content;
});
const content = editor.getValue()
this.$emit("input", content)
this.cacheValue = content
if (this.lint) this.provideLinting(content)
})
// Disable linting, if lint prop is false
if (this.lint) this.provideLinting(this.value)
},
methods: {
defineTheme() {
if (this.theme) {
return this.theme;
return this.theme
}
return (
this.$store.state.postwoman.settings.THEME_ACE_EDITOR || DEFAULT_THEME
);
}
return this.$store.state.postwoman.settings.THEME_ACE_EDITOR || DEFAULT_THEME
},
provideLinting: debounce(function(code) {
if (this.lang === "json") {
try {
jsonParse(code)
this.editor.session.setAnnotations([])
} catch (e) {
const pos = this.editor.session.getDocument().indexToPosition(e.start, 0)
this.editor.session.setAnnotations([
{
row: pos.row,
column: pos.column,
text: e.message,
type: "error",
},
])
}
}
}, 2000),
},
destroyed() {
this.editor.destroy();
}
};
this.editor.destroy()
},
}
</script>

View File

@@ -76,42 +76,42 @@
</style>
<script>
const KEY_TAB = 9;
const KEY_ESC = 27;
const KEY_TAB = 9
const KEY_ESC = 27
const KEY_ARROW_UP = 38;
const KEY_ARROW_DOWN = 40;
const KEY_ARROW_UP = 38
const KEY_ARROW_DOWN = 40
export default {
props: {
spellcheck: {
type: Boolean,
default: true,
required: false
required: false,
},
placeholder: {
type: String,
default: "",
required: false
required: false,
},
source: {
type: Array,
required: true
required: true,
},
value: {
type: String,
default: "",
required: false
}
required: false,
},
},
watch: {
text() {
this.$emit("input", this.text);
}
this.$emit("input", this.text)
},
},
data() {
@@ -120,69 +120,67 @@ export default {
selectionStart: 0,
suggestionsOffsetLeft: 0,
currentSuggestionIndex: -1,
suggestionsVisible: false
};
suggestionsVisible: false,
}
},
methods: {
updateSuggestions(event) {
// Hide suggestions if ESC pressed.
if (event.which && event.which === KEY_ESC) {
event.preventDefault();
this.suggestionsVisible = false;
this.currentSuggestionIndex = -1;
return;
event.preventDefault()
this.suggestionsVisible = false
this.currentSuggestionIndex = -1
return
}
// As suggestions is a reactive property, this implicitly
// causes suggestions to update.
this.selectionStart = this.$refs.acInput.selectionStart;
this.suggestionsOffsetLeft = 12 * this.selectionStart;
this.suggestionsVisible = true;
this.selectionStart = this.$refs.acInput.selectionStart
this.suggestionsOffsetLeft = 12 * this.selectionStart
this.suggestionsVisible = true
},
forceSuggestion(text) {
let input = this.text.substring(0, this.selectionStart);
this.text = input + text;
let input = this.text.substring(0, this.selectionStart)
this.text = input + text
this.selectionStart = this.text.length;
this.suggestionsVisible = true;
this.currentSuggestionIndex = -1;
this.selectionStart = this.text.length
this.suggestionsVisible = true
this.currentSuggestionIndex = -1
},
handleKeystroke(event) {
switch (event.which) {
case KEY_ARROW_UP:
event.preventDefault();
event.preventDefault()
this.currentSuggestionIndex =
this.currentSuggestionIndex - 1 >= 0
? this.currentSuggestionIndex - 1
: 0;
break;
this.currentSuggestionIndex - 1 >= 0 ? this.currentSuggestionIndex - 1 : 0
break
case KEY_ARROW_DOWN:
event.preventDefault();
event.preventDefault()
this.currentSuggestionIndex =
this.currentSuggestionIndex < this.suggestions.length - 1
? this.currentSuggestionIndex + 1
: this.suggestions.length - 1;
break;
: this.suggestions.length - 1
break
case KEY_TAB:
event.preventDefault();
event.preventDefault()
let activeSuggestion = this.suggestions[
this.currentSuggestionIndex >= 0 ? this.currentSuggestionIndex : 0
];
]
if (activeSuggestion) {
let input = this.text.substring(0, this.selectionStart);
this.text = input + activeSuggestion;
let input = this.text.substring(0, this.selectionStart)
this.text = input + activeSuggestion
}
break;
break
default:
break;
break
}
}
},
},
computed: {
@@ -192,7 +190,7 @@ export default {
* @returns {default.props.source|{type, required}}
*/
suggestions() {
let input = this.text.substring(0, this.selectionStart);
let input = this.text.substring(0, this.selectionStart)
return (
this.source
@@ -200,20 +198,20 @@ export default {
return (
entry.toLowerCase().startsWith(input.toLowerCase()) &&
input.toLowerCase() !== entry.toLowerCase()
);
)
})
// Cut off the part that's already been typed.
.map(entry => entry.substring(this.selectionStart))
// We only want the top 6 suggestions.
.slice(0, 6)
);
}
)
},
},
mounted() {
this.updateSuggestions({
target: this.$refs.acInput
});
}
};
target: this.$refs.acInput,
})
},
}
</script>

View File

@@ -43,44 +43,42 @@
</template>
<script>
import { fb } from "../../functions/fb";
import { fb } from "../../functions/fb"
export default {
props: {
show: Boolean
show: Boolean,
},
components: {
modal: () => import("../../components/modal")
modal: () => import("../../components/modal"),
},
data() {
return {
name: undefined
};
name: undefined,
}
},
methods: {
syncCollections() {
if (fb.currentUser !== null) {
if (fb.currentSettings[0].value) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections))
);
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
}
}
},
addNewCollection() {
if (!this.$data.name) {
this.$toast.info($t("invalid_collection_name"));
return;
this.$toast.info($t("invalid_collection_name"))
return
}
this.$store.commit("postwoman/addNewCollection", {
name: this.$data.name
});
this.$emit("hide-modal");
this.syncCollections();
name: this.$data.name,
})
this.$emit("hide-modal")
this.syncCollections()
},
hideModal() {
this.$emit("hide-modal");
}
}
};
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -47,27 +47,27 @@ export default {
props: {
show: Boolean,
collection: Object,
collectionIndex: Number
collectionIndex: Number,
},
components: {
modal: () => import("../../components/modal")
modal: () => import("../../components/modal"),
},
data() {
return {
name: undefined
};
name: undefined,
}
},
methods: {
addNewFolder() {
this.$store.commit("postwoman/addNewFolder", {
folder: { name: this.$data.name },
collectionIndex: this.$props.collectionIndex
});
this.hideModal();
collectionIndex: this.$props.collectionIndex,
})
this.hideModal()
},
hideModal() {
this.$emit("hide-modal");
}
}
};
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -21,11 +21,7 @@
</button>
</div>
<div>
<button
class="icon"
@click="$emit('edit-collection')"
v-close-popover
>
<button class="icon" @click="$emit('edit-collection')" v-close-popover>
<i class="material-icons">create</i>
<span>{{ $t("edit") }}</span>
</button>
@@ -51,11 +47,7 @@
@edit-request="$emit('edit-request', $event)"
/>
</li>
<li
v-if="
collection.folders.length === 0 && collection.requests.length === 0
"
>
<li v-if="collection.folders.length === 0 && collection.requests.length === 0">
<label>{{ $t("collection_empty") }}</label>
</li>
</ul>
@@ -71,7 +63,7 @@
request,
collectionIndex,
folderIndex: undefined,
requestIndex: index
requestIndex: index,
})
"
/>
@@ -98,31 +90,31 @@ ul li {
export default {
components: {
folder: () => import("./folder"),
request: () => import("./request")
request: () => import("./request"),
},
props: {
collectionIndex: Number,
collection: Object
collection: Object,
},
data() {
return {
showChildren: false,
selectedFolder: {}
};
selectedFolder: {},
}
},
methods: {
toggleShowChildren() {
this.showChildren = !this.showChildren;
this.showChildren = !this.showChildren
},
removeCollection() {
if (!confirm("Are you sure you want to remove this Collection?")) return;
if (!confirm("Are you sure you want to remove this Collection?")) return
this.$store.commit("postwoman/removeCollection", {
collectionIndex: this.collectionIndex
});
collectionIndex: this.collectionIndex,
})
},
editFolder(collectionIndex, folder, folderIndex) {
this.$emit("edit-folder", { collectionIndex, folder, folderIndex });
}
}
};
this.$emit("edit-folder", { collectionIndex, folder, folderIndex })
},
},
}
</script>

View File

@@ -47,35 +47,35 @@ export default {
props: {
show: Boolean,
editingCollection: Object,
editingCollectionIndex: Number
editingCollectionIndex: Number,
},
components: {
modal: () => import("../../components/modal")
modal: () => import("../../components/modal"),
},
data() {
return {
name: undefined
};
name: undefined,
}
},
methods: {
saveCollection() {
if (!this.$data.name) {
this.$toast.info($t("invalid_collection_name"));
return;
this.$toast.info($t("invalid_collection_name"))
return
}
const collectionUpdated = {
...this.$props.editingCollection,
name: this.$data.name
};
name: this.$data.name,
}
this.$store.commit("postwoman/editCollection", {
collection: collectionUpdated,
collectionIndex: this.$props.editingCollectionIndex
});
this.$emit("hide-modal");
collectionIndex: this.$props.editingCollectionIndex,
})
this.$emit("hide-modal")
},
hideModal() {
this.$emit("hide-modal");
}
}
};
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -17,12 +17,7 @@
<div slot="body">
<ul>
<li>
<input
type="text"
v-model="name"
:placeholder="folder.name"
@keyup.enter="editFolder"
/>
<input type="text" v-model="name" :placeholder="folder.name" @keyup.enter="editFolder" />
</li>
</ul>
</div>
@@ -49,28 +44,28 @@ export default {
collection: Object,
collectionIndex: Number,
folder: Object,
folderIndex: Number
folderIndex: Number,
},
components: {
modal: () => import("../../components/modal")
modal: () => import("../../components/modal"),
},
data() {
return {
name: undefined
};
name: undefined,
}
},
methods: {
editFolder() {
this.$store.commit("postwoman/editFolder", {
collectionIndex: this.$props.collectionIndex,
folder: { ...this.$props.folder, name: this.$data.name },
folderIndex: this.$props.folderIndex
});
this.hideModal();
folderIndex: this.$props.folderIndex,
})
this.hideModal()
},
hideModal() {
this.$emit("hide-modal");
}
}
};
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -27,22 +27,12 @@
/>
<label for="selectCollection">{{ $t("collection") }}</label>
<span class="select-wrapper">
<select
type="text"
id="selectCollection"
v-model="requestUpdateData.collectionIndex"
>
<select type="text" id="selectCollection" v-model="requestUpdateData.collectionIndex">
<option :key="undefined" :value="undefined" hidden disabled selected>{{
$t("current_collection")
}}</option>
<option
:key="undefined"
:value="undefined"
hidden
disabled
selected
>{{ $t("current_collection") }}</option
>
<option
v-for="(collection, index) in $store.state.postwoman
.collections"
v-for="(collection, index) in $store.state.postwoman.collections"
:key="index"
:value="index"
>
@@ -52,17 +42,9 @@
</span>
<label for="selectFolder">{{ $t("folder") }}</label>
<span class="select-wrapper">
<select
type="text"
id="selectFolder"
v-model="requestUpdateData.folderIndex"
>
<select type="text" id="selectFolder" v-model="requestUpdateData.folderIndex">
<option :key="undefined" :value="undefined">/</option>
<option
v-for="(folder, index) in folders"
:key="index"
:value="index"
>
<option v-for="(folder, index) in folders" :key="index" :value="index">
{{ folder.name }}
</option>
</select>
@@ -93,42 +75,39 @@ export default {
collectionIndex: Number,
folderIndex: Number,
request: Object,
requestIndex: Number
requestIndex: Number,
},
components: {
modal: () => import("../../components/modal")
modal: () => import("../../components/modal"),
},
data() {
return {
requestUpdateData: {
name: undefined,
collectionIndex: undefined,
folderIndex: undefined
}
};
folderIndex: undefined,
},
}
},
watch: {
"requestUpdateData.collectionIndex": function resetFolderIndex() {
// if user choosen some folder, than selected other collection, which doesn't have any folders
// than `requestUpdateData.folderIndex` won't be reseted
this.$data.requestUpdateData.folderIndex = undefined;
}
this.$data.requestUpdateData.folderIndex = undefined
},
},
computed: {
folders() {
const userSelectedAnyCollection =
this.$data.requestUpdateData.collectionIndex !== undefined;
if (!userSelectedAnyCollection) return [];
const userSelectedAnyCollection = this.$data.requestUpdateData.collectionIndex !== undefined
if (!userSelectedAnyCollection) return []
return this.$store.state.postwoman.collections[
this.$data.requestUpdateData.collectionIndex
].folders;
}
return this.$store.state.postwoman.collections[this.$data.requestUpdateData.collectionIndex]
.folders
},
},
methods: {
saveRequest() {
const userSelectedAnyCollection =
this.$data.requestUpdateData.collectionIndex !== undefined;
const userSelectedAnyCollection = this.$data.requestUpdateData.collectionIndex !== undefined
const requestUpdated = {
...this.$props.request,
@@ -136,8 +115,8 @@ export default {
collection: userSelectedAnyCollection
? this.$data.requestUpdateData.collectionIndex
: this.$props.collectionIndex,
folder: this.$data.requestUpdateData.folderIndex
};
folder: this.$data.requestUpdateData.folderIndex,
}
// pass data separately to don't depend on request's collection, folder fields
// probably, they should be deprecated because they don't describe request itself
@@ -147,14 +126,14 @@ export default {
requestOldIndex: this.$props.requestIndex,
requestNew: requestUpdated,
requestNewCollectionIndex: requestUpdated.collection,
requestNewFolderIndex: requestUpdated.folder
});
requestNewFolderIndex: requestUpdated.folder,
})
this.hideModal();
this.hideModal()
},
hideModal() {
this.$emit("hide-modal");
}
}
};
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -43,7 +43,7 @@
request,
collectionIndex,
folderIndex,
requestIndex: index
requestIndex: index,
})
"
/>
@@ -74,33 +74,33 @@ export default {
props: {
folder: Object,
collectionIndex: Number,
folderIndex: Number
folderIndex: Number,
},
components: {
request: () => import("./request")
request: () => import("./request"),
},
data() {
return {
showChildren: false
};
showChildren: false,
}
},
methods: {
toggleShowChildren() {
this.showChildren = !this.showChildren;
this.showChildren = !this.showChildren
},
selectRequest(request) {
this.$store.commit("postwoman/selectRequest", { request });
this.$store.commit("postwoman/selectRequest", { request })
},
removeFolder() {
if (!confirm("Are you sure you want to remove this folder?")) return;
if (!confirm("Are you sure you want to remove this folder?")) return
this.$store.commit("postwoman/removeFolder", {
collectionIndex: this.collectionIndex,
folderIndex: this.folderIndex
});
folderIndex: this.folderIndex,
})
},
editFolder() {
this.$emit("edit-folder");
}
}
};
this.$emit("edit-folder")
},
},
}
</script>

View File

@@ -14,16 +14,10 @@
<div class="flex-wrap">
<span
v-tooltip="{
content: !fb.currentUser
? $t('login_first')
: $t('replace_current')
content: !fb.currentUser ? $t('login_first') : $t('replace_current'),
}"
>
<button
:disabled="!fb.currentUser"
class="icon"
@click="syncCollections"
>
<button :disabled="!fb.currentUser" class="icon" @click="syncCollections">
<i class="material-icons">folder_shared</i>
<span>{{ $t("import_from_sync") }}</span>
</button>
@@ -72,11 +66,7 @@
<button class="icon" @click="hideModal">
{{ $t("cancel") }}
</button>
<button
class="icon primary"
@click="exportJSON"
v-tooltip="$t('download_file')"
>
<button class="icon primary" @click="exportJSON" v-tooltip="$t('download_file')">
{{ $t("export") }}
</button>
</span>
@@ -86,120 +76,106 @@
</template>
<script>
import { fb } from "../../functions/fb";
import { fb } from "../../functions/fb"
export default {
data() {
return {
fb
};
fb,
}
},
props: {
show: Boolean
show: Boolean,
},
components: {
modal: () => import("../../components/modal")
modal: () => import("../../components/modal"),
},
computed: {
collectionJson() {
return JSON.stringify(this.$store.state.postwoman.collections, null, 2);
}
return JSON.stringify(this.$store.state.postwoman.collections, null, 2)
},
},
methods: {
hideModal() {
this.$emit("hide-modal");
this.$emit("hide-modal")
},
openDialogChooseFileToReplaceWith() {
this.$refs.inputChooseFileToReplaceWith.click();
this.$refs.inputChooseFileToReplaceWith.click()
},
openDialogChooseFileToImportFrom() {
this.$refs.inputChooseFileToImportFrom.click();
this.$refs.inputChooseFileToImportFrom.click()
},
replaceWithJSON() {
let reader = new FileReader();
let reader = new FileReader()
reader.onload = event => {
let content = event.target.result;
let collections = JSON.parse(content);
let content = event.target.result
let collections = JSON.parse(content)
if (collections[0]) {
let [name, folders, requests] = Object.keys(collections[0]);
if (
name === "name" &&
folders === "folders" &&
requests === "requests"
) {
let [name, folders, requests] = Object.keys(collections[0])
if (name === "name" && folders === "folders" && requests === "requests") {
// Do nothing
}
} else if (
collections.info &&
collections.info.schema.includes("v2.1.0")
) {
collections = this.parsePostmanCollection(collections);
} else if (collections.info && collections.info.schema.includes("v2.1.0")) {
collections = this.parsePostmanCollection(collections)
} else {
return this.failedImport();
return this.failedImport()
}
this.$store.commit("postwoman/importCollections", collections);
this.fileImported();
};
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0]);
this.$store.commit("postwoman/importCollections", collections)
this.fileImported()
}
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0])
},
importFromJSON() {
let reader = new FileReader();
let reader = new FileReader()
reader.onload = event => {
let content = event.target.result;
let collections = JSON.parse(content);
let content = event.target.result
let collections = JSON.parse(content)
if (collections[0]) {
let [name, folders, requests] = Object.keys(collections[0]);
if (
name === "name" &&
folders === "folders" &&
requests === "requests"
) {
let [name, folders, requests] = Object.keys(collections[0])
if (name === "name" && folders === "folders" && requests === "requests") {
// Do nothing
}
} else if (
collections.info &&
collections.info.schema.includes("v2.1.0")
) {
collections = this.parsePostmanCollection(collections);
} else if (collections.info && collections.info.schema.includes("v2.1.0")) {
collections = this.parsePostmanCollection(collections)
} else {
return this.failedImport();
return this.failedImport()
}
this.$store.commit("postwoman/importCollections", collections);
this.fileImported();
};
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0]);
this.$store.commit("postwoman/importCollections", collections)
this.fileImported()
}
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0])
},
exportJSON() {
let text = this.collectionJson;
text = text.replace(/\n/g, "\r\n");
let text = this.collectionJson
text = text.replace(/\n/g, "\r\n")
let blob = new Blob([text], {
type: "text/json"
});
let anchor = document.createElement("a");
anchor.download = "postwoman-collection.json";
anchor.href = window.URL.createObjectURL(blob);
anchor.target = "_blank";
anchor.style.display = "none";
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
type: "text/json",
})
let anchor = document.createElement("a")
anchor.download = "postwoman-collection.json"
anchor.href = window.URL.createObjectURL(blob)
anchor.target = "_blank"
anchor.style.display = "none"
document.body.appendChild(anchor)
anchor.click()
document.body.removeChild(anchor)
this.$toast.success(this.$t("download_started"), {
icon: "done"
});
icon: "done",
})
},
syncCollections() {
this.$store.commit("postwoman/replaceCollections", fb.currentCollections);
this.fileImported();
this.$store.commit("postwoman/replaceCollections", fb.currentCollections)
this.fileImported()
},
fileImported() {
this.$toast.info(this.$t("file_imported"), {
icon: "folder_shared"
});
icon: "folder_shared",
})
},
failedImport() {
this.$toast.error(this.$t("import_failed"), {
icon: "error"
});
icon: "error",
})
},
parsePostmanCollection(collection, folders = true) {
let postwomanCollection = folders
@@ -207,37 +183,29 @@ export default {
{
name: "",
folders: [],
requests: []
}
requests: [],
},
]
: {
name: "",
requests: []
};
requests: [],
}
for (let collectionItem of collection.item) {
if (collectionItem.request) {
if (postwomanCollection[0]) {
postwomanCollection[0].name = collection.info
? collection.info.name
: "";
postwomanCollection[0].requests.push(
this.parsePostmanRequest(collectionItem)
);
postwomanCollection[0].name = collection.info ? collection.info.name : ""
postwomanCollection[0].requests.push(this.parsePostmanRequest(collectionItem))
} else {
postwomanCollection.name = collection.name ? collection.name : "";
postwomanCollection.requests.push(
this.parsePostmanRequest(collectionItem)
);
postwomanCollection.name = collection.name ? collection.name : ""
postwomanCollection.requests.push(this.parsePostmanRequest(collectionItem))
}
} else if (collectionItem.item) {
if (collectionItem.item[0]) {
postwomanCollection[0].folders.push(
this.parsePostmanCollection(collectionItem, false)
);
postwomanCollection[0].folders.push(this.parsePostmanCollection(collectionItem, false))
}
}
}
return postwomanCollection;
return postwomanCollection
},
parsePostmanRequest(requestObject) {
let pwRequest = {
@@ -256,69 +224,63 @@ export default {
rawInput: false,
contentType: "",
requestType: "",
name: ""
};
name: "",
}
pwRequest.name = requestObject.name;
pwRequest.name = requestObject.name
let requestObjectUrl = requestObject.request.url.raw.match(
/^(.+:\/\/[^\/]+|{[^\/]+})(\/[^\?]+|).*$/
);
pwRequest.url = requestObjectUrl[1];
pwRequest.path = requestObjectUrl[2] ? requestObjectUrl[2] : "";
pwRequest.method = requestObject.request.method;
let itemAuth = requestObject.request.auth
? requestObject.request.auth
: "";
let authType = itemAuth ? itemAuth.type : "";
)
pwRequest.url = requestObjectUrl[1]
pwRequest.path = requestObjectUrl[2] ? requestObjectUrl[2] : ""
pwRequest.method = requestObject.request.method
let itemAuth = requestObject.request.auth ? requestObject.request.auth : ""
let authType = itemAuth ? itemAuth.type : ""
if (authType === "basic") {
pwRequest.auth = "Basic Auth";
pwRequest.auth = "Basic Auth"
pwRequest.httpUser =
itemAuth.basic[0].key === "username"
? itemAuth.basic[0].value
: itemAuth.basic[1].value;
itemAuth.basic[0].key === "username" ? itemAuth.basic[0].value : itemAuth.basic[1].value
pwRequest.httpPassword =
itemAuth.basic[0].key === "password"
? itemAuth.basic[0].value
: itemAuth.basic[1].value;
itemAuth.basic[0].key === "password" ? itemAuth.basic[0].value : itemAuth.basic[1].value
} else if (authType === "oauth2") {
pwRequest.auth = "OAuth 2.0";
pwRequest.auth = "OAuth 2.0"
pwRequest.bearerToken =
itemAuth.oauth2[0].key === "accessToken"
? itemAuth.oauth2[0].value
: itemAuth.oauth2[1].value;
: itemAuth.oauth2[1].value
} else if (authType === "bearer") {
pwRequest.auth = "Bearer Token";
pwRequest.bearerToken = itemAuth.bearer[0].value;
pwRequest.auth = "Bearer Token"
pwRequest.bearerToken = itemAuth.bearer[0].value
}
let requestObjectHeaders = requestObject.request.header;
let requestObjectHeaders = requestObject.request.header
if (requestObjectHeaders) {
pwRequest.headers = requestObjectHeaders;
pwRequest.headers = requestObjectHeaders
for (let header of pwRequest.headers) {
delete header.name;
delete header.type;
delete header.name
delete header.type
}
}
let requestObjectParams = requestObject.request.url.query;
let requestObjectParams = requestObject.request.url.query
if (requestObjectParams) {
pwRequest.params = requestObjectParams;
pwRequest.params = requestObjectParams
for (let param of pwRequest.params) {
delete param.disabled;
delete param.disabled
}
}
if (requestObject.request.body) {
if (requestObject.request.body.mode === "urlencoded") {
let params = requestObject.request.body.urlencoded;
pwRequest.bodyParams = params ? params : [];
let params = requestObject.request.body.urlencoded
pwRequest.bodyParams = params ? params : []
for (let param of pwRequest.bodyParams) {
delete param.type;
delete param.type
}
} else if (requestObject.request.body.mode === "raw") {
pwRequest.rawInput = true;
pwRequest.rawParams = requestObject.request.body.raw;
pwRequest.rawInput = true
pwRequest.rawParams = requestObject.request.body.raw
}
}
return pwRequest;
}
}
};
return pwRequest
},
},
}
</script>

View File

@@ -107,8 +107,8 @@ ul {
</style>
<script>
import collection from "./collection";
import { fb } from "../../functions/fb";
import collection from "./collection"
import { fb } from "../../functions/fb"
export default {
components: {
@@ -120,7 +120,7 @@ export default {
editFolder: () => import("./editFolder"),
editRequest: () => import("./editRequest"),
importExportCollections: () => import("./importExportCollections"),
VirtualList: () => import("vue-virtual-scroll-list")
VirtualList: () => import("vue-virtual-scroll-list"),
},
data() {
return {
@@ -135,100 +135,98 @@ export default {
editingFolder: undefined,
editingFolderIndex: undefined,
editingRequest: undefined,
editingRequestIndex: undefined
};
editingRequestIndex: undefined,
}
},
computed: {
collections() {
return this.$store.state.postwoman.collections;
}
return this.$store.state.postwoman.collections
},
},
async mounted() {
this._keyListener = function(e) {
if (e.key === "Escape") {
e.preventDefault();
this.showModalAdd = this.showModalEdit = this.showModalImportExport = this.showModalAddFolder = this.showModalEditFolder = this.showModalEditRequest = false;
e.preventDefault()
this.showModalAdd = this.showModalEdit = this.showModalImportExport = this.showModalAddFolder = this.showModalEditFolder = this.showModalEditRequest = false
}
};
document.addEventListener("keydown", this._keyListener.bind(this));
}
document.addEventListener("keydown", this._keyListener.bind(this))
},
methods: {
displayModalAdd(shouldDisplay) {
this.showModalAdd = shouldDisplay;
this.showModalAdd = shouldDisplay
},
displayModalEdit(shouldDisplay) {
this.showModalEdit = shouldDisplay;
this.showModalEdit = shouldDisplay
if (!shouldDisplay) this.resetSelectedData();
if (!shouldDisplay) this.resetSelectedData()
},
displayModalImportExport(shouldDisplay) {
this.showModalImportExport = shouldDisplay;
this.showModalImportExport = shouldDisplay
},
displayModalAddFolder(shouldDisplay) {
this.showModalAddFolder = shouldDisplay;
this.showModalAddFolder = shouldDisplay
if (!shouldDisplay) this.resetSelectedData();
if (!shouldDisplay) this.resetSelectedData()
},
displayModalEditFolder(shouldDisplay) {
this.showModalEditFolder = shouldDisplay;
this.showModalEditFolder = shouldDisplay
if (!shouldDisplay) this.resetSelectedData();
if (!shouldDisplay) this.resetSelectedData()
},
displayModalEditRequest(shouldDisplay) {
this.showModalEditRequest = shouldDisplay;
this.showModalEditRequest = shouldDisplay
if (!shouldDisplay) this.resetSelectedData();
if (!shouldDisplay) this.resetSelectedData()
},
editCollection(collection, collectionIndex) {
this.$data.editingCollection = collection;
this.$data.editingCollectionIndex = collectionIndex;
this.displayModalEdit(true);
this.syncCollections();
this.$data.editingCollection = collection
this.$data.editingCollectionIndex = collectionIndex
this.displayModalEdit(true)
this.syncCollections()
},
addFolder(collection, collectionIndex) {
this.$data.editingCollection = collection;
this.$data.editingCollectionIndex = collectionIndex;
this.displayModalAddFolder(true);
this.syncCollections();
this.$data.editingCollection = collection
this.$data.editingCollectionIndex = collectionIndex
this.displayModalAddFolder(true)
this.syncCollections()
},
editFolder(payload) {
const { collectionIndex, folder, folderIndex } = payload;
this.$data.editingCollection = collection;
this.$data.editingCollectionIndex = collectionIndex;
this.$data.editingFolder = folder;
this.$data.editingFolderIndex = folderIndex;
this.displayModalEditFolder(true);
this.syncCollections();
const { collectionIndex, folder, folderIndex } = payload
this.$data.editingCollection = collection
this.$data.editingCollectionIndex = collectionIndex
this.$data.editingFolder = folder
this.$data.editingFolderIndex = folderIndex
this.displayModalEditFolder(true)
this.syncCollections()
},
editRequest(payload) {
const { request, collectionIndex, folderIndex, requestIndex } = payload;
this.$data.editingCollectionIndex = collectionIndex;
this.$data.editingFolderIndex = folderIndex;
this.$data.editingRequest = request;
this.$data.editingRequestIndex = requestIndex;
this.displayModalEditRequest(true);
this.syncCollections();
const { request, collectionIndex, folderIndex, requestIndex } = payload
this.$data.editingCollectionIndex = collectionIndex
this.$data.editingFolderIndex = folderIndex
this.$data.editingRequest = request
this.$data.editingRequestIndex = requestIndex
this.displayModalEditRequest(true)
this.syncCollections()
},
resetSelectedData() {
this.$data.editingCollection = undefined;
this.$data.editingCollectionIndex = undefined;
this.$data.editingFolder = undefined;
this.$data.editingFolderIndex = undefined;
this.$data.editingRequest = undefined;
this.$data.editingRequestIndex = undefined;
this.$data.editingCollection = undefined
this.$data.editingCollectionIndex = undefined
this.$data.editingFolder = undefined
this.$data.editingFolderIndex = undefined
this.$data.editingRequest = undefined
this.$data.editingRequestIndex = undefined
},
syncCollections() {
if (fb.currentUser !== null) {
if (fb.currentSettings[0].value) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections))
);
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
}
}
}
},
},
beforeDestroy() {
document.removeEventListener("keydown", this._keyListener);
}
};
document.removeEventListener("keydown", this._keyListener)
},
}
</script>

View File

@@ -1,11 +1,7 @@
<template>
<div class="flex-wrap">
<div>
<button
class="icon"
@click="selectRequest()"
v-tooltip="$t('use_request')"
>
<button class="icon" @click="selectRequest()" v-tooltip="$t('use_request')">
<i class="material-icons">insert_drive_file</i>
<span>{{ request.name }}</span>
</button>
@@ -51,20 +47,20 @@ export default {
request: Object,
collectionIndex: Number,
folderIndex: Number,
requestIndex: Number
requestIndex: Number,
},
methods: {
selectRequest() {
this.$store.commit("postwoman/selectRequest", { request: this.request });
this.$store.commit("postwoman/selectRequest", { request: this.request })
},
removeRequest() {
if (!confirm("Are you sure you want to remove this request?")) return;
if (!confirm("Are you sure you want to remove this request?")) return
this.$store.commit("postwoman/removeRequest", {
collectionIndex: this.collectionIndex,
folderIndex: this.folderIndex,
requestIndex: this.requestIndex
});
}
}
};
requestIndex: this.requestIndex,
})
},
},
}
</script>

View File

@@ -27,22 +27,12 @@
/>
<label for="selectCollection">{{ $t("collection") }}</label>
<span class="select-wrapper">
<select
type="text"
id="selectCollection"
v-model="requestData.collectionIndex"
>
<select type="text" id="selectCollection" v-model="requestData.collectionIndex">
<option :key="undefined" :value="undefined" hidden disabled selected>{{
$t("select_collection")
}}</option>
<option
:key="undefined"
:value="undefined"
hidden
disabled
selected
>{{ $t("select_collection") }}</option
>
<option
v-for="(collection, index) in $store.state.postwoman
.collections"
v-for="(collection, index) in $store.state.postwoman.collections"
:key="index"
:value="index"
>
@@ -52,34 +42,18 @@
</span>
<label for="selectFolder">{{ $t("folder") }}</label>
<span class="select-wrapper">
<select
type="text"
id="selectFolder"
v-model="requestData.folderIndex"
>
<select type="text" id="selectFolder" v-model="requestData.folderIndex">
<option :key="undefined" :value="undefined">/</option>
<option
v-for="(folder, index) in folders"
:key="index"
:value="index"
>
<option v-for="(folder, index) in folders" :key="index" :value="index">
{{ folder.name }}
</option>
</select>
</span>
<label for="selectRequest">{{ $t("request") }}</label>
<span class="select-wrapper">
<select
type="text"
id="selectRequest"
v-model="requestData.requestIndex"
>
<select type="text" id="selectRequest" v-model="requestData.requestIndex">
<option :key="undefined" :value="undefined">/</option>
<option
v-for="(folder, index) in requests"
:key="index"
:value="index"
>
<option v-for="(folder, index) in requests" :key="index" :value="index">
{{ folder.name }}
</option>
</select>
@@ -104,15 +78,15 @@
</template>
<script>
import { fb } from "../../functions/fb";
import { fb } from "../../functions/fb"
export default {
props: {
show: Boolean,
editingRequest: Object
editingRequest: Object,
},
components: {
modal: () => import("../../components/modal")
modal: () => import("../../components/modal"),
},
data() {
return {
@@ -121,106 +95,96 @@ export default {
name: undefined,
collectionIndex: undefined,
folderIndex: undefined,
requestIndex: undefined
}
};
requestIndex: undefined,
},
}
},
watch: {
"requestData.collectionIndex": function resetFolderAndRequestIndex() {
// if user choosen some folder, than selected other collection, which doesn't have any folders
// than `requestUpdateData.folderIndex` won't be reseted
this.$data.requestData.folderIndex = undefined;
this.$data.requestData.requestIndex = undefined;
this.$data.requestData.folderIndex = undefined
this.$data.requestData.requestIndex = undefined
},
"requestData.folderIndex": function resetRequestIndex() {
this.$data.requestData.requestIndex = undefined;
}
this.$data.requestData.requestIndex = undefined
},
},
computed: {
folders() {
const userSelectedAnyCollection =
this.$data.requestData.collectionIndex !== undefined;
if (!userSelectedAnyCollection) return [];
const userSelectedAnyCollection = this.$data.requestData.collectionIndex !== undefined
if (!userSelectedAnyCollection) return []
const noCollectionAvailable =
this.$store.state.postwoman.collections[
this.$data.requestData.collectionIndex
] !== undefined;
if (!noCollectionAvailable) return [];
this.$store.state.postwoman.collections[this.$data.requestData.collectionIndex] !==
undefined
if (!noCollectionAvailable) return []
return this.$store.state.postwoman.collections[
this.$data.requestData.collectionIndex
].folders;
return this.$store.state.postwoman.collections[this.$data.requestData.collectionIndex].folders
},
requests() {
const userSelectedAnyCollection =
this.$data.requestData.collectionIndex !== undefined;
if (!userSelectedAnyCollection) return [];
const userSelectedAnyCollection = this.$data.requestData.collectionIndex !== undefined
if (!userSelectedAnyCollection) return []
const userSelectedAnyFolder =
this.$data.requestData.folderIndex !== undefined;
const userSelectedAnyFolder = this.$data.requestData.folderIndex !== undefined
if (userSelectedAnyFolder) {
const collection = this.$store.state.postwoman.collections[
this.$data.requestData.collectionIndex
];
const folder = collection.folders[this.$data.requestData.folderIndex];
const requests = folder.requests;
return requests;
]
const folder = collection.folders[this.$data.requestData.folderIndex]
const requests = folder.requests
return requests
} else {
const collection = this.$store.state.postwoman.collections[
this.$data.requestData.collectionIndex
];
]
const noCollectionAvailable =
this.$store.state.postwoman.collections[
this.$data.requestData.collectionIndex
] !== undefined;
if (!noCollectionAvailable) return [];
this.$store.state.postwoman.collections[this.$data.requestData.collectionIndex] !==
undefined
if (!noCollectionAvailable) return []
const requests = collection.requests;
return requests;
const requests = collection.requests
return requests
}
}
},
},
methods: {
syncCollections() {
if (fb.currentUser !== null) {
if (fb.currentSettings[0].value) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections))
);
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
}
}
},
saveRequestAs() {
const userDidntSpecifyCollection =
this.$data.requestData.collectionIndex === undefined;
const userDidntSpecifyCollection = this.$data.requestData.collectionIndex === undefined
if (userDidntSpecifyCollection) {
this.$toast.error(this.$t("select_collection"), {
icon: "error"
});
return;
icon: "error",
})
return
}
const requestUpdated = {
...this.$props.editingRequest,
name: this.$data.requestData.name || this.$data.defaultRequestName,
collection: this.$data.requestData.collectionIndex
};
collection: this.$data.requestData.collectionIndex,
}
this.$store.commit("postwoman/saveRequestAs", {
request: requestUpdated,
collectionIndex: this.$data.requestData.collectionIndex,
folderIndex: this.$data.requestData.folderIndex,
requestIndex: this.$data.requestData.requestIndex
});
requestIndex: this.$data.requestData.requestIndex,
})
this.hideModal();
this.syncCollections();
this.hideModal()
this.syncCollections()
},
hideModal() {
this.$emit("hide-modal");
this.$emit("hide-model"); // for backward compatibility // TODO: use fixed event
}
}
};
this.$emit("hide-modal")
this.$emit("hide-model") // for backward compatibility // TODO: use fixed event
},
},
}
</script>

View File

@@ -43,52 +43,50 @@
</template>
<script>
import { fb } from "../../functions/fb";
import { fb } from "../../functions/fb"
export default {
props: {
show: Boolean
show: Boolean,
},
components: {
modal: () => import("../../components/modal")
modal: () => import("../../components/modal"),
},
data() {
return {
name: undefined
};
name: undefined,
}
},
methods: {
syncEnvironments() {
if (fb.currentUser !== null) {
if (fb.currentSettings[1].value) {
fb.writeEnvironments(
JSON.parse(JSON.stringify(this.$store.state.postwoman.environments))
);
fb.writeEnvironments(JSON.parse(JSON.stringify(this.$store.state.postwoman.environments)))
}
}
},
addNewEnvironment() {
if (!this.$data.name) {
this.$toast.info(this.$t("invalid_environment_name"));
return;
this.$toast.info(this.$t("invalid_environment_name"))
return
}
let newEnvironment = [
{
name: this.$data.name,
variables: []
}
];
variables: [],
},
]
this.$store.commit("postwoman/importAddEnvironments", {
environments: newEnvironment,
confirmation: "Environment added"
});
this.$emit("hide-modal");
this.syncEnvironments();
confirmation: "Environment added",
})
this.$emit("hide-modal")
this.syncEnvironments()
},
hideModal() {
this.$data.name = undefined;
this.$emit("hide-modal");
}
}
};
this.$data.name = undefined
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -30,11 +30,7 @@
<div class="flex-wrap">
<label for="variableList">{{ $t("env_variable_list") }}</label>
<div>
<button
class="icon"
@click="clearContent($event)"
v-tooltip.bottom="$t('clear')"
>
<button class="icon" @click="clearContent($event)" v-tooltip.bottom="$t('clear')">
<i class="material-icons">clear_all</i>
</button>
</div>
@@ -49,10 +45,7 @@
></textarea>
</li>
</ul>
<ul
v-for="(variable, index) in this.editingEnvCopy.variables"
:key="index"
>
<ul v-for="(variable, index) in this.editingEnvCopy.variables" :key="index">
<li>
<input
:placeholder="$t('parameter_count', { count: index + 1 })"
@@ -61,7 +54,7 @@
@change="
$store.commit('postwoman/setVariableKey', {
index,
value: $event.target.value
value: $event.target.value,
})
"
autofocus
@@ -72,14 +65,12 @@
:placeholder="$t('value_count', { count: index + 1 })"
:name="'value' + index"
:value="
typeof variable.value === 'string'
? variable.value
: JSON.stringify(variable.value)
typeof variable.value === 'string' ? variable.value : JSON.stringify(variable.value)
"
@change="
$store.commit('postwoman/setVariableValue', {
index,
value: $event.target.value
value: $event.target.value,
})
"
/>
@@ -123,99 +114,94 @@
</template>
<script>
import textareaAutoHeight from "../../directives/textareaAutoHeight";
import textareaAutoHeight from "../../directives/textareaAutoHeight"
export default {
directives: {
textareaAutoHeight
textareaAutoHeight,
},
props: {
show: Boolean,
editingEnvironment: Object,
editingEnvironmentIndex: Number
editingEnvironmentIndex: Number,
},
components: {
modal: () => import("../../components/modal")
modal: () => import("../../components/modal"),
},
data() {
return {
name: undefined
};
name: undefined,
}
},
watch: {
editingEnvironment: function(update) {
this.name = this.$props.editingEnvironment && this.$props.editingEnvironment.name
? this.$props.editingEnvironment.name
: undefined
this.$store.commit(
"postwoman/setEditingEnvironment",
this.$props.editingEnvironment
);
}
this.name =
this.$props.editingEnvironment && this.$props.editingEnvironment.name
? this.$props.editingEnvironment.name
: undefined
this.$store.commit("postwoman/setEditingEnvironment", this.$props.editingEnvironment)
},
},
computed: {
editingEnvCopy() {
return this.$store.state.postwoman.editingEnvironment;
return this.$store.state.postwoman.editingEnvironment
},
variableString() {
const result = this.editingEnvCopy.variables;
return result === "" ? "" : JSON.stringify(result);
}
const result = this.editingEnvCopy.variables
return result === "" ? "" : JSON.stringify(result)
},
},
methods: {
clearContent(e) {
this.$store.commit("postwoman/removeVariables", []);
e.target.innerHTML = this.doneButton;
this.$store.commit("postwoman/removeVariables", [])
e.target.innerHTML = this.doneButton
this.$toast.info(this.$t("cleared"), {
icon: "clear_all"
});
setTimeout(
() => (e.target.innerHTML = '<i class="material-icons">clear_all</i>'),
1000
);
icon: "clear_all",
})
setTimeout(() => (e.target.innerHTML = '<i class="material-icons">clear_all</i>'), 1000)
},
addEnvironmentVariable() {
let value = { key: "", value: "" };
this.$store.commit("postwoman/addVariable", value);
let value = { key: "", value: "" }
this.$store.commit("postwoman/addVariable", value)
},
removeEnvironmentVariable(index) {
let variableIndex = index;
const oldVariables = this.editingEnvCopy.variables.slice();
let variableIndex = index
const oldVariables = this.editingEnvCopy.variables.slice()
const newVariables = this.editingEnvCopy.variables.filter(
(variable, index) => variableIndex !== index
);
)
this.$store.commit("postwoman/removeVariable", newVariables);
this.$store.commit("postwoman/removeVariable", newVariables)
this.$toast.error(this.$t("deleted"), {
icon: "delete",
action: {
text: this.$t("undo"),
onClick: (e, toastObject) => {
this.$store.commit("postwoman/removeVariable", oldVariables);
toastObject.remove();
}
}
});
this.$store.commit("postwoman/removeVariable", oldVariables)
toastObject.remove()
},
},
})
},
saveEnvironment() {
if (!this.$data.name) {
this.$toast.info(this.$t("invalid_environment_name"));
return;
this.$toast.info(this.$t("invalid_environment_name"))
return
}
const environmentUpdated = {
...this.editingEnvCopy,
name: this.$data.name
};
name: this.$data.name,
}
this.$store.commit("postwoman/saveEnvironment", {
environment: environmentUpdated,
environmentIndex: this.$props.editingEnvironmentIndex
});
this.$emit("hide-modal");
environmentIndex: this.$props.editingEnvironmentIndex,
})
this.$emit("hide-modal")
},
hideModal() {
this.$data.name = undefined;
this.$emit("hide-modal");
}
}
};
this.$data.name = undefined
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -1,11 +1,7 @@
<template>
<div class="flex-wrap">
<div>
<button
class="icon"
@click="$emit('select-environment')"
v-tooltip="$t('use_environment')"
>
<button class="icon" @click="$emit('select-environment')" v-tooltip="$t('use_environment')">
<i class="material-icons">insert_drive_file</i>
<span>{{ environment.name }}</span>
</button>
@@ -16,11 +12,7 @@
</button>
<template slot="popover">
<div>
<button
class="icon"
@click="$emit('edit-environment')"
v-close-popover
>
<button class="icon" @click="$emit('edit-environment')" v-close-popover>
<i class="material-icons">create</i>
<span>{{ $t("edit") }}</span>
</button>
@@ -53,13 +45,13 @@ ul li {
export default {
props: {
environment: Object,
environmentIndex: Number
environmentIndex: Number,
},
methods: {
removeEnvironment() {
if (!confirm("Are you sure you want to remove this environment?")) return;
this.$store.commit("postwoman/removeEnvironment", this.environmentIndex);
}
}
};
if (!confirm("Are you sure you want to remove this environment?")) return
this.$store.commit("postwoman/removeEnvironment", this.environmentIndex)
},
},
}
</script>

View File

@@ -14,16 +14,10 @@
<div class="flex-wrap">
<span
v-tooltip="{
content: !fb.currentUser
? $t('login_first')
: $t('replace_current')
content: !fb.currentUser ? $t('login_first') : $t('replace_current'),
}"
>
<button
:disabled="!fb.currentUser"
class="icon"
@click="syncEnvironments"
>
<button :disabled="!fb.currentUser" class="icon" @click="syncEnvironments">
<i class="material-icons">folder_shared</i>
<span>{{ $t("import_from_sync") }}</span>
</button>
@@ -72,11 +66,7 @@
<button class="icon" @click="hideModal">
{{ $t("cancel") }}
</button>
<button
class="icon primary"
@click="exportJSON"
v-tooltip="$t('download_file')"
>
<button class="icon primary" @click="exportJSON" v-tooltip="$t('download_file')">
{{ $t("export") }}
</button>
</span>
@@ -86,88 +76,85 @@
</template>
<script>
import { fb } from "../../functions/fb";
import { fb } from "../../functions/fb"
export default {
data() {
return {
fb
};
fb,
}
},
props: {
show: Boolean
show: Boolean,
},
components: {
modal: () => import("../../components/modal")
modal: () => import("../../components/modal"),
},
computed: {
environmentJson() {
return JSON.stringify(this.$store.state.postwoman.environments, null, 2);
}
return JSON.stringify(this.$store.state.postwoman.environments, null, 2)
},
},
methods: {
hideModal() {
this.$emit("hide-modal");
this.$emit("hide-modal")
},
openDialogChooseFileToReplaceWith() {
this.$refs.inputChooseFileToReplaceWith.click();
this.$refs.inputChooseFileToReplaceWith.click()
},
openDialogChooseFileToImportFrom() {
this.$refs.inputChooseFileToImportFrom.click();
this.$refs.inputChooseFileToImportFrom.click()
},
replaceWithJSON() {
let reader = new FileReader();
let reader = new FileReader()
reader.onload = event => {
let content = event.target.result;
let environments = JSON.parse(content);
this.$store.commit("postwoman/replaceEnvironments", environments);
};
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0]);
this.fileImported();
let content = event.target.result
let environments = JSON.parse(content)
this.$store.commit("postwoman/replaceEnvironments", environments)
}
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0])
this.fileImported()
},
importFromJSON() {
let reader = new FileReader();
let reader = new FileReader()
reader.onload = event => {
let content = event.target.result;
let environments = JSON.parse(content);
let content = event.target.result
let environments = JSON.parse(content)
let confirmation = this.$t("file_imported")
this.$store.commit("postwoman/importAddEnvironments", {
environments,
confirmation
});
};
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0]);
confirmation,
})
}
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0])
},
exportJSON() {
let text = this.environmentJson;
text = text.replace(/\n/g, "\r\n");
let text = this.environmentJson
text = text.replace(/\n/g, "\r\n")
let blob = new Blob([text], {
type: "text/json"
});
let anchor = document.createElement("a");
anchor.download = "postwoman-environment.json";
anchor.href = window.URL.createObjectURL(blob);
anchor.target = "_blank";
anchor.style.display = "none";
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
type: "text/json",
})
let anchor = document.createElement("a")
anchor.download = "postwoman-environment.json"
anchor.href = window.URL.createObjectURL(blob)
anchor.target = "_blank"
anchor.style.display = "none"
document.body.appendChild(anchor)
anchor.click()
document.body.removeChild(anchor)
this.$toast.success(this.$t("download_started"), {
icon: "done"
});
icon: "done",
})
},
syncEnvironments() {
this.$store.commit(
"postwoman/replaceEnvironments",
fb.currentEnvironments
);
this.fileImported();
this.$store.commit("postwoman/replaceEnvironments", fb.currentEnvironments)
this.fileImported()
},
fileImported() {
this.$toast.info(this.$t("file_imported"), {
icon: "folder_shared"
});
}
}
};
icon: "folder_shared",
})
},
},
}
</script>

View File

@@ -1,10 +1,5 @@
<template>
<pw-section
class="green"
icon="history"
:label="$t('environment')"
ref="environment"
>
<pw-section class="green" icon="history" :label="$t('environment')" ref="environment">
<addEnvironment :show="showModalAdd" @hide-modal="displayModalAdd(false)" />
<editEnvironment
:show="showModalEdit"
@@ -39,10 +34,7 @@
:remain="Math.min(5, environments.length)"
>
<ul>
<li
v-for="(environment, index) in environments"
:key="environment.name"
>
<li v-for="(environment, index) in environments" :key="environment.name">
<environment
:environmentIndex="index"
:environment="environment"
@@ -70,11 +62,11 @@ ul {
</style>
<script>
import environment from "./environment";
import { fb } from "../../functions/fb";
import environment from "./environment"
import { fb } from "../../functions/fb"
const updateOnLocalStorage = (propertyName, property) =>
window.localStorage.setItem(propertyName, JSON.stringify(property));
window.localStorage.setItem(propertyName, JSON.stringify(property))
export default {
components: {
@@ -83,7 +75,7 @@ export default {
addEnvironment: () => import("./addEnvironment"),
editEnvironment: () => import("./editEnvironment"),
importExportEnvironment: () => import("./importExportEnvironment"),
VirtualList: () => import("vue-virtual-scroll-list")
VirtualList: () => import("vue-virtual-scroll-list"),
},
data() {
return {
@@ -91,57 +83,55 @@ export default {
showModalAdd: false,
showModalEdit: false,
editingEnvironment: undefined,
editingEnvironmentIndex: undefined
};
editingEnvironmentIndex: undefined,
}
},
computed: {
environments() {
return this.$store.state.postwoman.environments;
}
return this.$store.state.postwoman.environments
},
},
async mounted() {
this._keyListener = function(e) {
if (e.key === "Escape") {
e.preventDefault();
this.showModalImportExport = false;
e.preventDefault()
this.showModalImportExport = false
}
};
document.addEventListener("keydown", this._keyListener.bind(this));
}
document.addEventListener("keydown", this._keyListener.bind(this))
},
methods: {
displayModalAdd(shouldDisplay) {
this.showModalAdd = shouldDisplay;
this.showModalAdd = shouldDisplay
},
displayModalEdit(shouldDisplay) {
this.showModalEdit = shouldDisplay;
this.showModalEdit = shouldDisplay
if (!shouldDisplay) this.resetSelectedData();
if (!shouldDisplay) this.resetSelectedData()
},
displayModalImportExport(shouldDisplay) {
this.showModalImportExport = shouldDisplay;
this.showModalImportExport = shouldDisplay
},
editEnvironment(environment, environmentIndex) {
this.$data.editingEnvironment = environment;
this.$data.editingEnvironmentIndex = environmentIndex;
this.displayModalEdit(true);
this.syncEnvironments;
this.$data.editingEnvironment = environment
this.$data.editingEnvironmentIndex = environmentIndex
this.displayModalEdit(true)
this.syncEnvironments
},
resetSelectedData() {
this.$data.editingEnvironment = undefined;
this.$data.editingEnvironmentIndex = undefined;
this.$data.editingEnvironment = undefined
this.$data.editingEnvironmentIndex = undefined
},
syncEnvironments() {
if (fb.currentUser !== null) {
if (fb.currentSettings[1].value) {
fb.writeEnvironments(
JSON.parse(JSON.stringify(this.$store.state.postwoman.environments))
);
fb.writeEnvironments(JSON.parse(JSON.stringify(this.$store.state.postwoman.environments)))
}
}
}
},
},
beforeDestroy() {
document.removeEventListener("keydown", this._keyListener);
}
};
document.removeEventListener("keydown", this._keyListener)
},
}
</script>

View File

@@ -55,24 +55,24 @@ ol {
</style>
<script>
import { fb } from "../../functions/fb";
import { fb } from "../../functions/fb"
export default {
components: {
VirtualList: () => import("vue-virtual-scroll-list")
VirtualList: () => import("vue-virtual-scroll-list"),
},
data() {
return {
fb
};
fb,
}
},
methods: {
deleteFeed(feed) {
fb.deleteFeed(feed.id);
fb.deleteFeed(feed.id)
this.$toast.error(this.$t("deleted"), {
icon: "delete"
});
}
}
};
icon: "delete",
})
},
},
}
</script>

View File

@@ -46,24 +46,24 @@ ol {
</style>
<script>
import { fb } from "../../functions/fb";
import { fb } from "../../functions/fb"
export default {
data() {
return {
message: null,
label: null
};
label: null,
}
},
methods: {
formPost() {
if (!(this.message || this.label)) {
return;
return
}
fb.writeFeeds(this.message, this.label);
this.message = null;
this.label = null;
}
}
};
fb.writeFeeds(this.message, this.label)
this.message = null
this.label = null
},
},
}
</script>

View File

@@ -41,19 +41,19 @@
</template>
<script>
import firebase from "firebase/app";
import { fb } from "../../functions/fb";
import firebase from "firebase/app"
import { fb } from "../../functions/fb"
export default {
data() {
return {
fb
};
fb,
}
},
methods: {
signInWithGoogle() {
const provider = new firebase.auth.GoogleAuthProvider();
const provider = new firebase.auth.GoogleAuthProvider()
firebase
.auth()
.signInWithPopup(provider)
@@ -66,23 +66,23 @@ export default {
action: {
text: this.$t("yes"),
onClick: (e, toastObject) => {
fb.writeSettings("syncHistory", false);
fb.writeSettings("syncCollections", true);
this.$router.push({ path: "/settings" });
toastObject.remove();
}
}
});
fb.writeSettings("syncHistory", false)
fb.writeSettings("syncCollections", true)
this.$router.push({ path: "/settings" })
toastObject.remove()
},
},
})
}
})
.catch(err => {
this.$toast.show(err.message || err, {
icon: "error"
});
});
icon: "error",
})
})
},
signInWithGithub() {
const provider = new firebase.auth.GithubAuthProvider();
const provider = new firebase.auth.GithubAuthProvider()
firebase
.auth()
.signInWithPopup(provider)
@@ -95,21 +95,21 @@ export default {
action: {
text: this.$t("yes"),
onClick: (e, toastObject) => {
fb.writeSettings("syncHistory", false);
fb.writeSettings("syncCollections", true);
this.$router.push({ path: "/settings" });
toastObject.remove();
}
}
});
fb.writeSettings("syncHistory", false)
fb.writeSettings("syncCollections", true)
this.$router.push({ path: "/settings" })
toastObject.remove()
},
},
})
}
})
.catch(err => {
this.$toast.show(err.message || err, {
icon: "error"
});
});
}
}
};
icon: "error",
})
})
},
},
}
</script>

View File

@@ -11,28 +11,28 @@
<style scoped lang="scss"></style>
<script>
import typelink from "./typelink";
import typelink from "./typelink"
export default {
components: {
typelink: typelink
typelink: typelink,
},
props: {
gqlArg: Object
gqlArg: Object,
},
computed: {
argName() {
return this.gqlArg.name;
return this.gqlArg.name
},
argType() {
return this.gqlArg.type;
}
return this.gqlArg.type
},
},
methods: {
jumpCallback(typeName) {}
}
};
jumpCallback(typeName) {},
},
}
</script>

View File

@@ -6,10 +6,7 @@
(
<span v-for="(field, index) in fieldArgs" :key="index">
{{ field.name }}:
<typelink
:gqlType="field.type"
:jumpTypeCallback="jumpTypeCallback"
/>
<typelink :gqlType="field.type" :jumpTypeCallback="jumpTypeCallback" />
<span v-if="index !== fieldArgs.length - 1">
,
</span>
@@ -53,16 +50,16 @@
</style>
<script>
import typelink from "./typelink";
import typelink from "./typelink"
export default {
components: {
typelink: typelink
typelink: typelink,
},
props: {
gqlField: Object,
jumpTypeCallback: Function
jumpTypeCallback: Function,
},
computed: {
@@ -73,21 +70,19 @@ export default {
`${arg.name}: ${arg.type.toString()}${
index !== this.gqlField.args.length - 1 ? ", " : ""
}`
);
}, "");
const argsString = args.length > 0 ? `(${args})` : "";
return `${
this.gqlField.name
}${argsString}: ${this.gqlField.type.toString()}`;
)
}, "")
const argsString = args.length > 0 ? `(${args})` : ""
return `${this.gqlField.name}${argsString}: ${this.gqlField.type.toString()}`
},
fieldName() {
return this.gqlField.name;
return this.gqlField.name
},
fieldArgs() {
return this.gqlField.args || [];
}
}
};
return this.gqlField.args || []
},
},
}
</script>

View File

@@ -19,33 +19,33 @@
</style>
<script>
const DEFAULT_THEME = "twilight";
const DEFAULT_THEME = "twilight"
import ace from "ace-builds";
import * as gql from "graphql";
import { getAutocompleteSuggestions } from "graphql-language-service-interface";
import "ace-builds/webpack-resolver";
import "ace-builds/src-noconflict/ext-language_tools";
import debounce from "../../functions/utils/debounce";
import ace from "ace-builds"
import * as gql from "graphql"
import { getAutocompleteSuggestions } from "graphql-language-service-interface"
import "ace-builds/webpack-resolver"
import "ace-builds/src-noconflict/ext-language_tools"
import debounce from "../../functions/utils/debounce"
export default {
props: {
value: {
type: String,
default: ""
default: "",
},
theme: {
type: String,
required: false
required: false,
},
lang: {
type: String,
default: "json"
default: "json",
},
options: {
type: Object,
default: {}
}
default: {},
},
},
data() {
@@ -53,15 +53,15 @@ export default {
initialized: false,
editor: null,
cacheValue: "",
validationSchema: null
};
validationSchema: null,
}
},
watch: {
value(value) {
if (value !== this.cacheValue) {
this.editor.session.setValue(value, 1);
this.cacheValue = value;
this.editor.session.setValue(value, 1)
this.cacheValue = value
}
},
theme() {
@@ -73,21 +73,28 @@ export default {
});
},
lang(value) {
this.editor.getSession().setMode(`ace/mode/${value}`);
this.editor.getSession().setMode(`ace/mode/${value}`)
},
options(value) {
this.editor.setOptions(value);
}
this.editor.setOptions(value)
},
},
mounted() {
let langTools = ace.require("ace/ext/language_tools");
let langTools = ace.require("ace/ext/language_tools")
const editor = ace.edit(this.$refs.editor, {
mode: `ace/mode/${this.lang}`,
enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
...this.options
...this.options,
})
// Set the theme and show the editor only after it's been set to prevent FOUC.
editor.setTheme(`ace/theme/${this.defineTheme()}`, () => {
this.$nextTick().then(() => {
this.initialized = true;
});
});
// Set the theme and show the editor only after it's been set to prevent FOUC.
@@ -98,19 +105,12 @@ export default {
});
const completer = {
getCompletions: (
editor,
_session,
{ row, column },
_prefix,
callback
) => {
getCompletions: (editor, _session, { row, column }, _prefix, callback) => {
if (this.validationSchema) {
const completions = getAutocompleteSuggestions(
this.validationSchema,
editor.getValue(),
{ line: row, character: column }
);
const completions = getAutocompleteSuggestions(this.validationSchema, editor.getValue(), {
line: row,
character: column,
})
callback(
null,
@@ -118,64 +118,60 @@ export default {
name: label,
value: label,
score: 1.0,
meta: detail
meta: detail,
}))
);
)
} else {
callback(null, []);
callback(null, [])
}
}
};
},
}
langTools.setCompleters([completer]);
langTools.setCompleters([completer])
if (this.value) editor.setValue(this.value, 1);
if (this.value) editor.setValue(this.value, 1)
this.editor = editor;
this.cacheValue = this.value;
this.editor = editor
this.cacheValue = this.value
editor.on("change", () => {
const content = editor.getValue();
this.$emit("input", content);
this.parseContents(content);
this.cacheValue = content;
});
const content = editor.getValue()
this.$emit("input", content)
this.parseContents(content)
this.cacheValue = content
})
this.parseContents(this.value);
this.parseContents(this.value)
},
methods: {
defineTheme() {
if (this.theme) {
return this.theme;
return this.theme
} else {
return (
this.$store.state.postwoman.settings.THEME_ACE_EDITOR || DEFAULT_THEME
);
return this.$store.state.postwoman.settings.THEME_ACE_EDITOR || DEFAULT_THEME
}
},
setValidationSchema(schema) {
this.validationSchema = schema;
this.parseContents(this.cacheValue);
this.validationSchema = schema
this.parseContents(this.cacheValue)
},
parseContents: debounce(function(content) {
if (content !== "") {
try {
const doc = gql.parse(content);
const doc = gql.parse(content)
if (this.validationSchema) {
this.editor.session.setAnnotations(
gql
.validate(this.validationSchema, doc)
.map(({ locations, message }) => ({
row: locations[0].line - 1,
column: locations[0].column - 1,
text: message,
type: "error"
}))
);
gql.validate(this.validationSchema, doc).map(({ locations, message }) => ({
row: locations[0].line - 1,
column: locations[0].column - 1,
text: message,
type: "error",
}))
)
}
} catch (e) {
this.editor.session.setAnnotations([
@@ -183,14 +179,14 @@ export default {
row: e.locations[0].line - 1,
column: e.locations[0].column - 1,
text: e.message,
type: "error"
}
]);
type: "error",
},
])
}
} else {
this.editor.session.setAnnotations([]);
this.editor.session.setAnnotations([])
}
}, 2000)
}, 2000),
},
beforeDestroy() {

View File

@@ -33,12 +33,12 @@
<script>
export default {
components: {
"gql-field": () => import("./field")
"gql-field": () => import("./field"),
},
props: {
gqlType: {},
jumpTypeCallback: Function
}
};
jumpTypeCallback: Function,
},
}
</script>

View File

@@ -16,19 +16,19 @@ export default {
props: {
gqlType: null,
// (typeName: string) => void
jumpTypeCallback: Function
jumpTypeCallback: Function,
},
computed: {
typeString() {
return this.gqlType.toString();
}
return this.gqlType.toString()
},
},
methods: {
jumpToType() {
this.jumpTypeCallback(this.gqlType);
}
}
};
this.jumpTypeCallback(this.gqlType)
},
},
}
</script>

View File

@@ -28,7 +28,7 @@
:class="{ stared: entry.star }"
@click="toggleStar(entry)"
v-tooltip="{
content: !entry.star ? $t('add_star') : $t('remove_star')
content: !entry.star ? $t('add_star') : $t('remove_star'),
}"
>
<i class="material-icons">
@@ -162,9 +162,7 @@
</transition>
</ul>
</virtual-list>
<ul
:class="{ hidden: filteredHistory.length != 0 || history.length === 0 }"
>
<ul :class="{ hidden: filteredHistory.length != 0 || history.length === 0 }">
<li>
<label>{{ $t("nothing_found") }} "{{ filterText }}"</label>
</li>
@@ -201,11 +199,7 @@
</button>
</div>
<div>
<button
class="icon"
@click="sort_by_status_code()"
v-close-popover
>
<button class="icon" @click="sort_by_status_code()" v-close-popover>
<i class="material-icons">assistant</i>
<span>{{ $t("status") }}</span>
</button>
@@ -326,16 +320,16 @@ ol {
</style>
<script>
import { findStatusGroup } from "../pages/index";
import { fb } from "../functions/fb";
import { findStatusGroup } from "../pages/index"
import { fb } from "../functions/fb"
const updateOnLocalStorage = (propertyName, property) =>
window.localStorage.setItem(propertyName, JSON.stringify(property));
window.localStorage.setItem(propertyName, JSON.stringify(property))
export default {
components: {
"pw-section": () => import("./section"),
VirtualList: () => import("vue-virtual-scroll-list")
VirtualList: () => import("vue-virtual-scroll-list"),
},
data() {
return {
@@ -351,166 +345,144 @@ export default {
reverse_sort_status_code: false,
reverse_sort_url: false,
reverse_sort_path: false,
showMore: false
};
showMore: false,
}
},
computed: {
filteredHistory() {
this.history =
fb.currentUser !== null
? fb.currentHistory
: JSON.parse(window.localStorage.getItem("history")) || [];
: JSON.parse(window.localStorage.getItem("history")) || []
return this.history.filter(entry => {
const filterText = this.filterText.toLowerCase();
const filterText = this.filterText.toLowerCase()
return Object.keys(entry).some(key => {
let value = entry[key];
value = typeof value !== "string" ? value.toString() : value;
return value.toLowerCase().includes(filterText);
});
});
}
let value = entry[key]
value = typeof value !== "string" ? value.toString() : value
return value.toLowerCase().includes(filterText)
})
})
},
},
methods: {
clearHistory() {
if (fb.currentUser !== null) {
fb.clearHistory();
fb.clearHistory()
}
this.history = [];
this.filterText = "";
this.disableHistoryClearing();
updateOnLocalStorage("history", this.history);
this.history = []
this.filterText = ""
this.disableHistoryClearing()
updateOnLocalStorage("history", this.history)
this.$toast.error(this.$t("history_deleted"), {
icon: "delete"
});
icon: "delete",
})
},
useHistory(entry) {
this.$emit("useHistory", entry);
this.$emit("useHistory", entry)
},
findEntryStatus(entry) {
const foundStatusGroup = findStatusGroup(entry.status);
const foundStatusGroup = findStatusGroup(entry.status)
return (
foundStatusGroup || {
className: ""
className: "",
}
);
)
},
deleteHistory(entry) {
if (fb.currentUser !== null) {
fb.deleteHistory(entry);
fb.deleteHistory(entry)
}
this.history.splice(this.history.indexOf(entry), 1);
this.history.splice(this.history.indexOf(entry), 1)
if (this.history.length === 0) {
this.filterText = "";
this.filterText = ""
}
updateOnLocalStorage("history", this.history);
updateOnLocalStorage("history", this.history)
this.$toast.error(this.$t("deleted"), {
icon: "delete"
});
icon: "delete",
})
},
addEntry(entry) {
this.history.push(entry);
updateOnLocalStorage("history", this.history);
this.history.push(entry)
updateOnLocalStorage("history", this.history)
},
enableHistoryClearing() {
if (!this.history || !this.history.length) return;
this.isClearingHistory = true;
if (!this.history || !this.history.length) return
this.isClearingHistory = true
},
disableHistoryClearing() {
this.isClearingHistory = false;
this.isClearingHistory = false
},
sort_by_time() {
let byDate = this.history.slice(0);
let byDate = this.history.slice(0)
byDate.sort((a, b) => {
let date_a = a.date.split("/");
let date_b = b.date.split("/");
let time_a = a.time.split(":");
let time_b = b.time.split(":");
let final_a = new Date(
date_a[2],
date_a[1],
date_a[0],
time_a[0],
time_a[1],
time_a[2]
);
let final_b = new Date(
date_b[2],
date_b[1],
date_b[0],
time_b[0],
time_b[1],
time_b[2]
);
if (this.reverse_sort_time) return final_b - final_a;
else return final_a - final_b;
});
this.history = byDate;
this.reverse_sort_time = !this.reverse_sort_time;
let date_a = a.date.split("/")
let date_b = b.date.split("/")
let time_a = a.time.split(":")
let time_b = b.time.split(":")
let final_a = new Date(date_a[2], date_a[1], date_a[0], time_a[0], time_a[1], time_a[2])
let final_b = new Date(date_b[2], date_b[1], date_b[0], time_b[0], time_b[1], time_b[2])
if (this.reverse_sort_time) return final_b - final_a
else return final_a - final_b
})
this.history = byDate
this.reverse_sort_time = !this.reverse_sort_time
},
sort_by_status_code() {
let byCode = this.history.slice(0);
let byCode = this.history.slice(0)
byCode.sort((a, b) => {
if (this.reverse_sort_status_code) return b.status - a.status;
else return a.status - b.status;
});
this.history = byCode;
this.reverse_sort_status_code = !this.reverse_sort_status_code;
if (this.reverse_sort_status_code) return b.status - a.status
else return a.status - b.status
})
this.history = byCode
this.reverse_sort_status_code = !this.reverse_sort_status_code
},
sort_by_url() {
let byUrl = this.history.slice(0);
let byUrl = this.history.slice(0)
byUrl.sort((a, b) => {
if (this.reverse_sort_url)
return a.url === b.url ? 0 : +(a.url < b.url) || -1;
else return a.url === b.url ? 0 : +(a.url > b.url) || -1;
});
this.history = byUrl;
this.reverse_sort_url = !this.reverse_sort_url;
if (this.reverse_sort_url) return a.url === b.url ? 0 : +(a.url < b.url) || -1
else return a.url === b.url ? 0 : +(a.url > b.url) || -1
})
this.history = byUrl
this.reverse_sort_url = !this.reverse_sort_url
},
sort_by_label() {
let byLabel = this.history.slice(0);
let byLabel = this.history.slice(0)
byLabel.sort((a, b) => {
if (this.reverse_sort_label)
return a.label === b.label ? 0 : +(a.label < b.label) || -1;
else return a.label === b.label ? 0 : +(a.label > b.label) || -1;
});
this.history = byLabel;
this.reverse_sort_label = !this.reverse_sort_label;
if (this.reverse_sort_label) return a.label === b.label ? 0 : +(a.label < b.label) || -1
else return a.label === b.label ? 0 : +(a.label > b.label) || -1
})
this.history = byLabel
this.reverse_sort_label = !this.reverse_sort_label
},
sort_by_path() {
let byPath = this.history.slice(0);
let byPath = this.history.slice(0)
byPath.sort((a, b) => {
if (this.reverse_sort_path)
return a.path === b.path ? 0 : +(a.path < b.path) || -1;
else return a.path === b.path ? 0 : +(a.path > b.path) || -1;
});
this.history = byPath;
this.reverse_sort_path = !this.reverse_sort_path;
if (this.reverse_sort_path) return a.path === b.path ? 0 : +(a.path < b.path) || -1
else return a.path === b.path ? 0 : +(a.path > b.path) || -1
})
this.history = byPath
this.reverse_sort_path = !this.reverse_sort_path
},
sort_by_duration() {
let byDuration = this.history.slice(0);
let byDuration = this.history.slice(0)
byDuration.sort((a, b) => {
if (this.reverse_sort_duration)
return a.duration === b.duration
? 0
: +(a.duration < b.duration) || -1;
else
return a.duration === b.duration
? 0
: +(a.duration > b.duration) || -1;
});
this.history = byDuration;
this.reverse_sort_duration = !this.reverse_sort_duration;
return a.duration === b.duration ? 0 : +(a.duration < b.duration) || -1
else return a.duration === b.duration ? 0 : +(a.duration > b.duration) || -1
})
this.history = byDuration
this.reverse_sort_duration = !this.reverse_sort_duration
},
toggleCollapse() {
this.showMore = !this.showMore;
this.showMore = !this.showMore
},
toggleStar(entry) {
if (fb.currentUser !== null) {
fb.toggleStar(entry, !entry.star);
fb.toggleStar(entry, !entry.star)
}
entry.star = !entry.star;
updateOnLocalStorage("history", this.history);
}
}
};
entry.star = !entry.star
updateOnLocalStorage("history", this.history)
},
},
}
</script>

View File

@@ -49,8 +49,8 @@
export default {
props: {
color: {
type: String
}
}
};
type: String,
},
},
}
</script>

View File

@@ -1,15 +1,12 @@
<template>
<fieldset
:id="label.toLowerCase()"
:class="{ 'no-colored-frames': !frameColorsEnabled }"
>
<fieldset :id="label.toLowerCase()" :class="{ 'no-colored-frames': !frameColorsEnabled }">
<legend @click.prevent="collapse">
<span>{{ label }}</span>
<i class="material-icons">
{{ isCollapsed ? "expand_more" : "expand_less" }}
{{ isCollapsed(label) ? "expand_more" : "expand_less" }}
</i>
</legend>
<div class="collapsible" :class="{ hidden: collapsed }">
<div class="collapsible" :class="{ hidden: isCollapsed(label.toLowerCase()) }">
<slot />
</div>
</fieldset>
@@ -25,32 +22,34 @@ fieldset.no-colored-frames legend {
export default {
computed: {
frameColorsEnabled() {
return this.$store.state.postwoman.settings.FRAME_COLORS_ENABLED || false;
}
},
data() {
return {
isCollapsed: false
};
return this.$store.state.postwoman.settings.FRAME_COLORS_ENABLED || false
},
sectionString() {
return `${this.$route.path.replace(/\/+$/, "")}/${this.label}`
},
},
props: {
label: {
type: String,
default: "Section"
default: "Section",
},
collapsed: {
type: Boolean
}
type: Boolean,
},
},
methods: {
collapse({ target }) {
const parent = target.parentNode.parentNode;
parent.querySelector(".collapsible").classList.toggle("hidden");
this.isCollapsed = !this.isCollapsed;
}
}
};
const parent = target.parentNode.parentNode
parent.querySelector(".collapsible").classList.toggle("hidden")
// Save collapsed section into the collapsedSections array
this.$store.commit("setCollapsedSection", this.sectionString)
},
isCollapsed(label) {
return this.$store.state.theme.collapsedSections.includes(this.sectionString) || false
},
},
}
</script>

View File

@@ -45,15 +45,15 @@ export default {
props: {
color: {
type: String,
required: true
required: true,
},
name: {
type: String
type: String,
},
active: {
type: Boolean,
default: false
}
}
};
default: false,
},
},
}
</script>

View File

@@ -84,15 +84,15 @@ export default {
props: {
on: {
type: Boolean,
default: false
}
default: false,
},
},
methods: {
toggle() {
const containsOnClass = this.$refs.toggle.classList.toggle("on");
this.$emit("change", containsOnClass);
}
}
};
const containsOnClass = this.$refs.toggle.classList.toggle("on")
this.$emit("change", containsOnClass)
},
},
}
</script>

View File

@@ -2,7 +2,7 @@ export default {
name: "textareaAutoHeight",
update({ scrollHeight, clientHeight, style }) {
if (scrollHeight !== clientHeight) {
style.minHeight = `${scrollHeight}px`;
style.minHeight = `${scrollHeight}px`
}
}
};
},
}

View File

@@ -10,11 +10,7 @@
"target": "postwoman",
"public": "dist",
"cleanUrls": true,
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"]
},
"storage": {
"rules": "storage.rules"

View File

@@ -23,4 +23,4 @@
// ]
"indexes": [],
"fieldOverrides": []
}
}

View File

@@ -3,9 +3,9 @@ const mimeToMode = {
"text/html": "html",
"application/xml": "xml",
"application/hal+json": "json",
"application/json": "json"
"application/json": "json",
}
export function getEditorLangForMimeType(mimeType) {
return mimeToMode[mimeType] || "plain_text";
return mimeToMode[mimeType] || "plain_text"
}

View File

@@ -1,6 +1,6 @@
import firebase from "firebase/app";
import "firebase/firestore";
import "firebase/auth";
import firebase from "firebase/app"
import "firebase/firestore"
import "firebase/auth"
// Initialize Firebase, copied from cloud console
const firebaseConfig = {
@@ -11,12 +11,12 @@ const firebaseConfig = {
storageBucket: "postwoman-api.appspot.com",
messagingSenderId: "421993993223",
appId: "1:421993993223:web:ec0baa8ee8c02ffa1fc6a2",
measurementId: "G-ERJ6025CEB"
};
firebase.initializeApp(firebaseConfig);
measurementId: "G-ERJ6025CEB",
}
firebase.initializeApp(firebaseConfig)
// a reference to the users collection
const usersCollection = firebase.firestore().collection("users");
const usersCollection = firebase.firestore().collection("users")
// the shared state object that any vue component
// can get access to
@@ -34,13 +34,13 @@ export const fb = {
author_name: fb.currentUser.displayName,
author_image: fb.currentUser.photoURL,
message,
label
};
label,
}
usersCollection
.doc(fb.currentUser.uid)
.collection("feeds")
.add(dt)
.catch(e => console.error("error inserting", dt, e));
.catch(e => console.error("error inserting", dt, e))
},
deleteFeed: id => {
usersCollection
@@ -48,7 +48,7 @@ export const fb = {
.collection("feeds")
.doc(id)
.delete()
.catch(e => console.error("error deleting", id, e));
.catch(e => console.error("error deleting", id, e))
},
writeSettings: async (setting, value) => {
const st = {
@@ -57,22 +57,22 @@ export const fb = {
author_name: fb.currentUser.displayName,
author_image: fb.currentUser.photoURL,
name: setting,
value
};
value,
}
usersCollection
.doc(fb.currentUser.uid)
.collection("settings")
.doc(setting)
.set(st)
.catch(e => console.error("error updating", st, e));
.catch(e => console.error("error updating", st, e))
},
writeHistory: async entry => {
const hs = entry;
const hs = entry
usersCollection
.doc(fb.currentUser.uid)
.collection("history")
.add(hs)
.catch(e => console.error("error inserting", hs, e));
.catch(e => console.error("error inserting", hs, e))
},
deleteHistory: entry => {
usersCollection
@@ -80,7 +80,7 @@ export const fb = {
.collection("history")
.doc(entry.id)
.delete()
.catch(e => console.error("error deleting", entry, e));
.catch(e => console.error("error deleting", entry, e))
},
clearHistory: () => {
usersCollection
@@ -88,8 +88,8 @@ export const fb = {
.collection("history")
.get()
.then(({ docs }) => {
docs.forEach(e => fb.deleteHistory(e));
});
docs.forEach(e => fb.deleteHistory(e))
})
},
toggleStar: (entry, value) => {
usersCollection
@@ -97,7 +97,7 @@ export const fb = {
.collection("history")
.doc(entry.id)
.update({ star: value })
.catch(e => console.error("error deleting", entry, e));
.catch(e => console.error("error deleting", entry, e))
},
writeCollections: async collection => {
const cl = {
@@ -105,14 +105,14 @@ export const fb = {
author: fb.currentUser.uid,
author_name: fb.currentUser.displayName,
author_image: fb.currentUser.photoURL,
collection: collection
};
collection: collection,
}
usersCollection
.doc(fb.currentUser.uid)
.collection("collections")
.doc("sync")
.set(cl)
.catch(e => console.error("error updating", cl, e));
.catch(e => console.error("error updating", cl, e))
},
writeEnvironments: async environment => {
const ev = {
@@ -120,21 +120,21 @@ export const fb = {
author: fb.currentUser.uid,
author_name: fb.currentUser.displayName,
author_image: fb.currentUser.photoURL,
environment: environment
};
environment: environment,
}
usersCollection
.doc(fb.currentUser.uid)
.collection("environments")
.doc("sync")
.set(ev)
.catch(e => console.error("error updating", ev, e));
}
};
.catch(e => console.error("error updating", ev, e))
},
}
// When a user logs in or out, save that in the store
firebase.auth().onAuthStateChanged(user => {
if (user) {
fb.currentUser = user;
fb.currentUser = user
fb.currentUser.providerData.forEach(profile => {
let us = {
updatedOn: new Date(),
@@ -142,80 +142,80 @@ firebase.auth().onAuthStateChanged(user => {
name: profile.displayName,
email: profile.email,
photoUrl: profile.photoURL,
uid: profile.uid
};
uid: profile.uid,
}
usersCollection
.doc(fb.currentUser.uid)
.set(us)
.catch(e => console.error("error updating", us, e));
});
.catch(e => console.error("error updating", us, e))
})
usersCollection
.doc(fb.currentUser.uid)
.collection("feeds")
.orderBy("createdOn", "desc")
.onSnapshot(feedsRef => {
const feeds = [];
const feeds = []
feedsRef.forEach(doc => {
const feed = doc.data();
feed.id = doc.id;
feeds.push(feed);
});
fb.currentFeeds = feeds;
});
const feed = doc.data()
feed.id = doc.id
feeds.push(feed)
})
fb.currentFeeds = feeds
})
usersCollection
.doc(fb.currentUser.uid)
.collection("settings")
.onSnapshot(settingsRef => {
const settings = [];
const settings = []
settingsRef.forEach(doc => {
const setting = doc.data();
setting.id = doc.id;
settings.push(setting);
});
fb.currentSettings = settings;
});
const setting = doc.data()
setting.id = doc.id
settings.push(setting)
})
fb.currentSettings = settings
})
usersCollection
.doc(fb.currentUser.uid)
.collection("history")
.onSnapshot(historyRef => {
const history = [];
const history = []
historyRef.forEach(doc => {
const entry = doc.data();
entry.id = doc.id;
history.push(entry);
});
fb.currentHistory = history;
});
const entry = doc.data()
entry.id = doc.id
history.push(entry)
})
fb.currentHistory = history
})
usersCollection
.doc(fb.currentUser.uid)
.collection("collections")
.onSnapshot(collectionsRef => {
const collections = [];
const collections = []
collectionsRef.forEach(doc => {
const collection = doc.data();
collection.id = doc.id;
collections.push(collection);
});
fb.currentCollections = collections[0].collection;
});
const collection = doc.data()
collection.id = doc.id
collections.push(collection)
})
fb.currentCollections = collections[0].collection
})
usersCollection
.doc(fb.currentUser.uid)
.collection("environments")
.onSnapshot(environmentsRef => {
const environments = [];
const environments = []
environmentsRef.forEach(doc => {
const environment = doc.data();
environment.id = doc.id;
environments.push(environment);
});
fb.currentEnvironments = environments[0].environment;
});
const environment = doc.data()
environment.id = doc.id
environments.push(environment)
})
fb.currentEnvironments = environments[0].environment
})
} else {
fb.currentUser = null;
fb.currentUser = null
}
});
})

View File

@@ -120,5 +120,5 @@ export const commonHeaders = [
"X-Pingback",
"X-Requested-With",
"X-Robots-Tag",
"X-UA-Compatible"
];
"X-UA-Compatible",
]

310
functions/jsonParse.js Normal file
View File

@@ -0,0 +1,310 @@
/**
* Copyright (c) 2019 GraphQL Contributors
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/**
* This JSON parser simply walks the input, generating an AST. Use this in lieu
* of JSON.parse if you need character offset parse errors and an AST parse tree
* with location information.
*
* If an error is encountered, a SyntaxError will be thrown, with properties:
*
* - message: string
* - start: int - the start inclusive offset of the syntax error
* - end: int - the end exclusive offset of the syntax error
*
*/
export default function jsonParse(str) {
string = str
strLen = str.length
start = end = lastEnd = -1
ch()
lex()
const ast = parseObj()
expect("EOF")
return ast
}
let string
let strLen
let start
let end
let lastEnd
let code
let kind
function parseObj() {
const nodeStart = start
const members = []
expect("{")
if (!skip("}")) {
do {
members.push(parseMember())
} while (skip(","))
expect("}")
}
return {
kind: "Object",
start: nodeStart,
end: lastEnd,
members,
}
}
function parseMember() {
const nodeStart = start
const key = kind === "String" ? curToken() : null
expect("String")
expect(":")
const value = parseVal()
return {
kind: "Member",
start: nodeStart,
end: lastEnd,
key,
value,
}
}
function parseArr() {
const nodeStart = start
const values = []
expect("[")
if (!skip("]")) {
do {
values.push(parseVal())
} while (skip(","))
expect("]")
}
return {
kind: "Array",
start: nodeStart,
end: lastEnd,
values,
}
}
function parseVal() {
switch (kind) {
case "[":
return parseArr()
case "{":
return parseObj()
case "String":
case "Number":
case "Boolean":
case "Null":
const token = curToken()
lex()
return token
}
return expect("Value")
}
function curToken() {
return { kind, start, end, value: JSON.parse(string.slice(start, end)) }
}
function expect(str) {
if (kind === str) {
lex()
return
}
let found
if (kind === "EOF") {
found = "[end of file]"
} else if (end - start > 1) {
found = "`" + string.slice(start, end) + "`"
} else {
const match = string.slice(start).match(/^.+?\b/)
found = "`" + (match ? match[0] : string[start]) + "`"
}
throw syntaxError(`Expected ${str} but found ${found}.`)
}
function syntaxError(message) {
return { message, start, end }
}
function skip(k) {
if (kind === k) {
lex()
return true
}
}
function ch() {
if (end < strLen) {
end++
code = end === strLen ? 0 : string.charCodeAt(end)
}
}
function lex() {
lastEnd = end
while (code === 9 || code === 10 || code === 13 || code === 32) {
ch()
}
if (code === 0) {
kind = "EOF"
return
}
start = end
switch (code) {
// "
case 34:
kind = "String"
return readString()
// -, 0-9
case 45:
case 48:
case 49:
case 50:
case 51:
case 52:
case 53:
case 54:
case 55:
case 56:
case 57:
kind = "Number"
return readNumber()
// f
case 102:
if (string.slice(start, start + 5) !== "false") {
break
}
end += 4
ch()
kind = "Boolean"
return
// n
case 110:
if (string.slice(start, start + 4) !== "null") {
break
}
end += 3
ch()
kind = "Null"
return
// t
case 116:
if (string.slice(start, start + 4) !== "true") {
break
}
end += 3
ch()
kind = "Boolean"
return
}
kind = string[start]
ch()
}
function readString() {
ch()
while (code !== 34 && code > 31) {
if (code === 92) {
// \
ch()
switch (code) {
case 34: // "
case 47: // /
case 92: // \
case 98: // b
case 102: // f
case 110: // n
case 114: // r
case 116: // t
ch()
break
case 117: // u
ch()
readHex()
readHex()
readHex()
readHex()
break
default:
throw syntaxError("Bad character escape sequence.")
}
} else if (end === strLen) {
throw syntaxError("Unterminated string.")
} else {
ch()
}
}
if (code === 34) {
ch()
return
}
throw syntaxError("Unterminated string.")
}
function readHex() {
if (
(code >= 48 && code <= 57) || // 0-9
(code >= 65 && code <= 70) || // A-F
(code >= 97 && code <= 102) // a-f
) {
return ch()
}
throw syntaxError("Expected hexadecimal digit.")
}
function readNumber() {
if (code === 45) {
// -
ch()
}
if (code === 48) {
// 0
ch()
} else {
readDigits()
}
if (code === 46) {
// .
ch()
readDigits()
}
if (code === 69 || code === 101) {
// E e
ch()
if (code === 43 || code === 45) {
// + -
ch()
}
readDigits()
}
}
function readDigits() {
if (code < 48 || code > 57) {
// 0 - 9
throw syntaxError("Expected decimal digit.")
}
do {
ch()
} while (code >= 48 && code <= 57) // 0 - 9
}

View File

@@ -1,20 +1,16 @@
import AxiosStrategy from "./strategies/AxiosStrategy";
import ExtensionStrategy, {
hasExtensionInstalled
} from "./strategies/ExtensionStrategy";
import FirefoxStrategy from "./strategies/FirefoxStrategy";
import ChromeStrategy, {
hasChromeExtensionInstalled
} from "./strategies/ChromeStrategy";
import AxiosStrategy from "./strategies/AxiosStrategy"
import ExtensionStrategy, { hasExtensionInstalled } from "./strategies/ExtensionStrategy"
import FirefoxStrategy from "./strategies/FirefoxStrategy"
import ChromeStrategy, { hasChromeExtensionInstalled } from "./strategies/ChromeStrategy"
const isExtensionsAllowed = ({ state }) =>
typeof state.postwoman.settings.EXTENSIONS_ENABLED === "undefined" ||
state.postwoman.settings.EXTENSIONS_ENABLED;
state.postwoman.settings.EXTENSIONS_ENABLED
const runAppropriateStrategy = (req, store) => {
if (isExtensionsAllowed(store)) {
if (hasExtensionInstalled()) {
return ExtensionStrategy(req, store);
return ExtensionStrategy(req, store)
}
// The following strategies are deprecated and kept to support older version of the extensions
@@ -22,21 +18,19 @@ const runAppropriateStrategy = (req, store) => {
// Chrome Provides a chrome object for scripts to access
// Check its availability to say whether you are in Google Chrome
if (window.chrome && hasChromeExtensionInstalled()) {
return ChromeStrategy(req, store);
return ChromeStrategy(req, store)
}
// The firefox plugin injects a function to send requests through it
// If that is available, then we can use the FirefoxStrategy
if (window.firefoxExtSendRequest) {
return FirefoxStrategy(req, store);
return FirefoxStrategy(req, store)
}
}
return AxiosStrategy(req, store);
};
return AxiosStrategy(req, store)
}
const sendNetworkRequest = (req, store) =>
runAppropriateStrategy(req, store).finally(() =>
window.$nuxt.$loading.finish()
);
runAppropriateStrategy(req, store).finally(() => window.$nuxt.$loading.finish())
export { sendNetworkRequest };
export { sendNetworkRequest }

View File

@@ -1,13 +1,13 @@
const PASS = "PASS";
const FAIL = "FAIL";
const ERROR = "ERROR";
const PASS = "PASS"
const FAIL = "FAIL"
const ERROR = "ERROR"
const styles = {
[PASS]: { icon: "check", class: "success-response" },
[FAIL]: { icon: "close", class: "cl-error-response" },
[ERROR]: { icon: "close", class: "cl-error-response" },
none: { icon: "", class: "" }
};
none: { icon: "", class: "" },
}
// TODO: probably have to use a more global state for `test`
@@ -18,158 +18,130 @@ export default function runTestScriptWithVariables(script, variables) {
_report: "",
expect(value) {
try {
return expect(value, this._testReports);
return expect(value, this._testReports)
} catch (e) {
pw._testReports.push({ result: ERROR, message: e });
pw._testReports.push({ result: ERROR, message: e })
}
},
test: (descriptor, func) => test(descriptor, func, pw._testReports)
test: (descriptor, func) => test(descriptor, func, pw._testReports),
// globals that the script is allowed to have access to.
};
Object.assign(pw, variables);
}
Object.assign(pw, variables)
// run pre-request script within this function so that it has access to the pw object.
new Function("pw", script)(pw);
new Function("pw", script)(pw)
//
const testReports = pw._testReports.map(item => {
if (item.result) {
item.styles = styles[item.result];
item.styles = styles[item.result]
} else {
item.styles = styles.none;
item.styles = styles.none
}
return item;
});
return { report: pw._report, errors: pw._errors, testResults: testReports };
return item
})
return { report: pw._report, errors: pw._errors, testResults: testReports }
}
function test(descriptor, func, _testReports) {
_testReports.push({ startBlock: descriptor });
_testReports.push({ startBlock: descriptor })
try {
func();
func()
} catch (e) {
_testReports.push({ result: ERROR, message: e });
_testReports.push({ result: ERROR, message: e })
}
_testReports.push({ endBlock: true });
_testReports.push({ endBlock: true })
// TODO: Organize and generate text report of each {descriptor: true} section in testReports.
// add checkmark or x depending on if each testReport is pass=true or pass=false
}
function expect(expectValue, _testReports) {
return new Expectation(expectValue, null, _testReports);
return new Expectation(expectValue, null, _testReports)
}
class Expectation {
constructor(expectValue, _not, _testReports) {
this.expectValue = expectValue;
this.not = _not || new Expectation(this.expectValue, true, _testReports);
this._testReports = _testReports; // this values is used within Test.it, which wraps Expectation and passes _testReports value.
this.expectValue = expectValue
this.not = _not || new Expectation(this.expectValue, true, _testReports)
this._testReports = _testReports // this values is used within Test.it, which wraps Expectation and passes _testReports value.
this._satisfies = function(expectValue, targetValue) {
// Used for testing if two values match the expectation, which could be === OR !==, depending on if not
// was used. Expectation#_satisfies prevents the need to have an if(this.not) branch in every test method.
// Signature is _satisfies([expectValue,] targetValue): if only one argument is given, it is assumed the targetValue, and expectValue is set to this.expectValue
if (!targetValue) {
targetValue = expectValue;
expectValue = this.expectValue;
targetValue = expectValue
expectValue = this.expectValue
}
if (this.not === true) {
// test the inverse. this.not is always truthly, but an Expectation that is inverted will always be strictly `true`
return expectValue !== targetValue;
return expectValue !== targetValue
} else {
return expectValue === targetValue;
return expectValue === targetValue
}
};
}
}
_fmtNot(message) {
// given a string with "(not)" in it, replaces with "not" or "", depending if the expectation is expecting the positive or inverse (this._not)
if (this.not === true) {
return message.replace("(not)", "not ");
return message.replace("(not)", "not ")
} else {
return message.replace("(not)", "");
return message.replace("(not)", "")
}
}
_fail(message) {
this._testReports.push({ result: FAIL, message });
this._testReports.push({ result: FAIL, message })
}
_pass(message) {
this._testReports.push({ result: PASS });
this._testReports.push({ result: PASS })
}
// TEST METHODS DEFINED BELOW
// these are the usual methods that would follow expect(...)
toBe(value) {
return this._satisfies(value)
? this._pass()
: this._fail(
this._fmtNot(`Expected ${this.expectValue} (not)to be ${value}`)
);
: this._fail(this._fmtNot(`Expected ${this.expectValue} (not)to be ${value}`))
}
toHaveProperty(value) {
return this._satisfies(this.expectValue.hasOwnProperty(value), true)
? this._pass()
: this._fail(
this._fmtNot(
`Expected object ${this.expectValue} to (not)have property ${value}`
)
);
this._fmtNot(`Expected object ${this.expectValue} to (not)have property ${value}`)
)
}
toBeLevel2xx() {
const code = parseInt(this.expectValue);
const code = parseInt(this.expectValue)
if (Number.isNaN(code)) {
return this._fail(
`Expected 200-level status but could not parse value ${this.expectValue}`
);
return this._fail(`Expected 200-level status but could not parse value ${this.expectValue}`)
}
return this._satisfies(code >= 200 && code < 300)
? this._pass()
: this._fail(
this._fmtNot(
`Expected ${this.expectValue} to (not)be 200-level status`
)
);
: this._fail(this._fmtNot(`Expected ${this.expectValue} to (not)be 200-level status`))
}
toBeLevel3xx() {
const code = parseInt(this.expectValue);
const code = parseInt(this.expectValue)
if (Number.isNaN(code)) {
return this._fail(
`Expected 300-level status but could not parse value ${this.expectValue}`
);
return this._fail(`Expected 300-level status but could not parse value ${this.expectValue}`)
}
return this._satisfies(code >= 300 && code < 400)
? this._pass()
: this._fail(
this._fmtNot(
`Expected ${this.expectValue} to (not)be 300-level status`
)
);
: this._fail(this._fmtNot(`Expected ${this.expectValue} to (not)be 300-level status`))
}
toBeLevel4xx() {
const code = parseInt(this.expectValue);
const code = parseInt(this.expectValue)
if (Number.isNaN(code)) {
return this._fail(
`Expected 400-level status but could not parse value ${this.expectValue}`
);
return this._fail(`Expected 400-level status but could not parse value ${this.expectValue}`)
}
return this._satisfies(code >= 400 && code < 500)
? this._pass()
: this._fail(
this._fmtNot(
`Expected ${this.expectValue} to (not)be 400-level status`
)
);
: this._fail(this._fmtNot(`Expected ${this.expectValue} to (not)be 400-level status`))
}
toBeLevel5xx() {
const code = parseInt(this.expectValue);
const code = parseInt(this.expectValue)
if (Number.isNaN(code)) {
return this._fail(
`Expected 500-level status but could not parse value ${this.expectValue}`
);
return this._fail(`Expected 500-level status but could not parse value ${this.expectValue}`)
}
return this._satisfies(code >= 500 && code < 600)
? this._pass()
: this._fail(
this._fmtNot(
`Expected ${this.expectValue} to (not)be 500-level status`
)
);
: this._fail(this._fmtNot(`Expected ${this.expectValue} to (not)be 500-level status`))
}
}

View File

@@ -1,20 +1,20 @@
export default function getEnvironmentVariablesFromScript(script) {
let _variables = {};
let _variables = {}
// the pw object is the proxy by which pre-request scripts can pass variables to the request.
// for security and control purposes, this is the only way a pre-request script should modify variables.
let pw = {
environment: {
set: (key, value) => (_variables[key] = value)
set: (key, value) => (_variables[key] = value),
},
env: {
set: (key, value) => (_variables[key] = value)
}
set: (key, value) => (_variables[key] = value),
},
// globals that the script is allowed to have access to.
};
}
// run pre-request script within this function so that it has access to the pw object.
new Function("pw", script)(pw);
new Function("pw", script)(pw)
return _variables;
return _variables
}

View File

@@ -1,23 +1,23 @@
import axios from "axios";
import axios from "axios"
const axiosWithProxy = async (req, { state }) => {
const { data } = await axios.post(
state.postwoman.settings.PROXY_URL || "https://postwoman.apollosoftware.xyz/",
req
);
return data;
};
)
return data
}
const axiosWithoutProxy = async (req, _store) => {
const res = await axios(req);
return res;
};
const res = await axios(req)
return res
}
const axiosStrategy = (req, store) => {
if (store.state.postwoman.settings.PROXY_ENABLED) {
return axiosWithProxy(req, store);
return axiosWithProxy(req, store)
}
return axiosWithoutProxy(req, store);
};
return axiosWithoutProxy(req, store)
}
export default axiosStrategy;
export default axiosStrategy

View File

@@ -1,10 +1,10 @@
const EXTENSION_ID = "amknoiejhlmhancpahfcfcfhllgkpbld";
const EXTENSION_ID = "amknoiejhlmhancpahfcfcfhllgkpbld"
// Check if the Chrome Extension is present
// The Chrome extension injects an empty span to help detection.
// Also check for the presence of window.chrome object to confirm smooth operations
export const hasChromeExtensionInstalled = () =>
document.getElementById("chromePWExtensionDetect") !== null;
document.getElementById("chromePWExtensionDetect") !== null
const chromeWithoutProxy = (req, _store) =>
new Promise((resolve, reject) => {
@@ -13,18 +13,18 @@ const chromeWithoutProxy = (req, _store) =>
{
messageType: "send-req",
data: {
config: req
}
config: req,
},
},
({ data }) => {
if (data.error) {
reject(data.error);
reject(data.error)
} else {
resolve(data.response);
resolve(data.response)
}
}
);
});
)
})
const chromeWithProxy = (req, { state }) =>
new Promise((resolve, reject) => {
@@ -44,20 +44,20 @@ const chromeWithProxy = (req, { state }) =>
},
({ data }) => {
if (data.error) {
reject(error);
reject(error)
} else {
resolve(data.response.data);
resolve(data.response.data)
}
}
);
});
)
})
const chromeStrategy = (req, store) => {
if (store.state.postwoman.settings.PROXY_ENABLED) {
return chromeWithProxy(req, store);
return chromeWithProxy(req, store)
} else {
return chromeWithoutProxy(req, store);
return chromeWithoutProxy(req, store)
}
};
}
export default chromeStrategy;
export default chromeStrategy

View File

@@ -1,5 +1,5 @@
export const hasExtensionInstalled = () =>
typeof window.__POSTWOMAN_EXTENSION_HOOK__ !== "undefined";
typeof window.__POSTWOMAN_EXTENSION_HOOK__ !== "undefined"
const extensionWithProxy = async (req, { state }) => {
const { data } = await window.__POSTWOMAN_EXTENSION_HOOK__.sendRequest({
@@ -12,15 +12,15 @@ const extensionWithProxy = async (req, { state }) => {
};
const extensionWithoutProxy = async (req, _store) => {
const res = await window.__POSTWOMAN_EXTENSION_HOOK__.sendRequest(req);
return res;
};
const res = await window.__POSTWOMAN_EXTENSION_HOOK__.sendRequest(req)
return res
}
const extensionStrategy = (req, store) => {
if (store.state.postwoman.settings.PROXY_ENABLED) {
return extensionWithProxy(req, store);
return extensionWithProxy(req, store)
}
return extensionWithoutProxy(req, store);
};
return extensionWithoutProxy(req, store)
}
export default extensionStrategy;
export default extensionStrategy

View File

@@ -1,16 +1,16 @@
const firefoxWithProxy = (req, { state }) =>
new Promise((resolve, reject) => {
const eventListener = event => {
window.removeEventListener("firefoxExtSendRequestComplete", event);
window.removeEventListener("firefoxExtSendRequestComplete", event)
if (event.detail.error) {
reject(JSON.parse(event.detail.error));
reject(JSON.parse(event.detail.error))
} else {
resolve(JSON.parse(event.detail.response).data);
resolve(JSON.parse(event.detail.response).data)
}
};
}
window.addEventListener("firefoxExtSendRequestComplete", eventListener);
window.addEventListener("firefoxExtSendRequestComplete", eventListener)
window.firefoxExtSendRequest({
method: "post",
@@ -23,28 +23,25 @@ const firefoxWithProxy = (req, { state }) =>
const firefoxWithoutProxy = (req, _store) =>
new Promise((resolve, reject) => {
const eventListener = ({ detail }) => {
window.removeEventListener(
"firefoxExtSendRequestComplete",
eventListener
);
window.removeEventListener("firefoxExtSendRequestComplete", eventListener)
if (detail.error) {
reject(JSON.parse(detail.error));
reject(JSON.parse(detail.error))
} else {
resolve(JSON.parse(detail.response));
resolve(JSON.parse(detail.response))
}
};
}
window.addEventListener("firefoxExtSendRequestComplete", eventListener);
window.addEventListener("firefoxExtSendRequestComplete", eventListener)
window.firefoxExtSendRequest(req);
});
window.firefoxExtSendRequest(req)
})
const firefoxStrategy = (req, store) => {
if (store.state.postwoman.settings.PROXY_ENABLED) {
return firefoxWithProxy(req, store);
return firefoxWithProxy(req, store)
}
return firefoxWithoutProxy(req, store);
};
return firefoxWithoutProxy(req, store)
}
export default firefoxStrategy;
export default firefoxStrategy

View File

@@ -1,7 +1,7 @@
export default function parseTemplateString(string, variables) {
if (!variables || !string) {
return string;
return string
}
const searchTerm = /<<([^>]*)>>/g; // "<<myVariable>>"
return string.replace(searchTerm, (match, p1) => variables[p1] || "");
const searchTerm = /<<([^>]*)>>/g // "<<myVariable>>"
return string.replace(searchTerm, (match, p1) => variables[p1] || "")
}

View File

@@ -3,13 +3,13 @@
// functions which might be called frequently
// NOTE : Don't use lambda functions as this doesn't get bound properly in them, use the 'function (args) {}' format
const debounce = (func, delay) => {
let inDebounce;
let inDebounce
return function() {
const context = this;
const args = arguments;
clearTimeout(inDebounce);
inDebounce = setTimeout(() => func.apply(context, args), delay);
};
};
const context = this
const args = arguments
clearTimeout(inDebounce)
inDebounce = setTimeout(() => func.apply(context, args), delay)
}
}
export default debounce;
export default debounce

View File

@@ -86,5 +86,5 @@ export default {
connect: "Verbinden",
disconnect: "Trennen",
start: "Start",
stop: "Stopp"
};
stop: "Stopp",
}

View File

@@ -85,8 +85,7 @@ export default {
enabled: "Enabled",
disabled: "Disabled",
proxy: "Proxy",
postwoman_official_proxy_hosting:
"Postwoman's Official Proxy is hosted by Apollo Software.",
postwoman_official_proxy_hosting: "Postwoman's Official Proxy is hosted by Apollo Software.",
read_the: "Read the",
apollosw_privacy_policy: "Apollo Software privacy policy",
contact_us: "Contact us",
@@ -162,8 +161,7 @@ export default {
disconnected_from: "Disconnected from {name}",
something_went_wrong: "Something went wrong!",
error_occurred: "An error has occurred.",
browser_support_sse:
"This browser doesn't seems to have Server Sent Events support.",
browser_support_sse: "This browser doesn't seems to have Server Sent Events support.",
log: "Log",
no_url: "No URL",
run_query: "Run Query",
@@ -216,8 +214,7 @@ export default {
value_count: "value {count}",
send_request_first: "Send a request first",
generate_docs: "Generate Documentation",
generate_docs_message:
"Import any Postwoman Collection to Generate Documentation on-the-go.",
generate_docs_message: "Import any Postwoman Collection to Generate Documentation on-the-go.",
generate_docs_first: "Generate documentation first",
docs_generated: "Documentation generated",
import_collections: "Import collections",
@@ -244,8 +241,7 @@ export default {
token_request_saved: "Token request saved",
donate_info1:
"If you have enjoyed the productivity of using Postwoman, consider donating as a sign of appreciation.",
donate_info2:
"You can support Postwoman development via the following methods:",
donate_info2: "You can support Postwoman development via the following methods:",
one_time_recurring: "One-time or recurring",
one_time: "One-time",
recurring: "Recurring",
@@ -256,8 +252,7 @@ export default {
enter_curl: "Enter cURL",
empty: "Empty",
extensions: "Extensions",
extensions_use_toggle:
"Use the browser extension to send requests (if present)",
extensions_use_toggle: "Use the browser extension to send requests (if present)",
extensions_info1: "Browser extension that simplifies access to Postwoman",
extensions_info2: "Get Postwoman browser extension!",
installed: "Installed",
@@ -274,5 +269,5 @@ export default {
login_first: "Login first",
paste_a_note: "Paste a note",
import_from_sync: "Import from Sync",
notes: "Notes"
};
notes: "Notes",
}

View File

@@ -78,8 +78,7 @@ export default {
enabled: "Habilitado",
disabled: "Deshabilitado",
proxy: "Proxy",
postwoman_official_proxy_hosting:
"Proxy Oficial de Postwoman está hospedado en Apollo Software.",
postwoman_official_proxy_hosting: "Proxy Oficial de Postwoman está hospedado en Apollo Software.",
read_the: "Leer la",
apollosw_privacy_policy: "Política de Privacidad de Apollo Software",
contact_us: "Contáctenos",
@@ -235,8 +234,7 @@ export default {
token_request_saved: "La petición de tToken ha sido guardada",
donate_info1:
"Si le ha gustado su productividad usando Postwoman, considere hacer una donación como un signo de su apreciación.",
donate_info2:
"Puede ayudar al desarrollo de Postwoman mediante los siguientes métodos:",
donate_info2: "Puede ayudar al desarrollo de Postwoman mediante los siguientes métodos:",
one_time_recurring: "Una vez o recurrente",
one_time: "Una vez",
recurring: "Recurrente",
@@ -260,5 +258,5 @@ export default {
turn_on: "Encender",
login_first: "Inicie sesión primero",
paste_a_collection: "Pegar una Colección",
import_from_sync: "Importar desde Sync"
};
import_from_sync: "Importar desde Sync",
}

View File

@@ -78,13 +78,12 @@ export default {
enabled: "فعال",
disabled: "غیر فعال",
proxy: "پراکسی",
postwoman_official_proxy_hosting:
"پراکسی Postwoman برروی هاست Apollo Software قرار دارد.",
postwoman_official_proxy_hosting: "پراکسی Postwoman برروی هاست Apollo Software قرار دارد.",
read_the: "بخوانید",
apollosw_privacy_policy: "خط مشی رازداری Apollo Software",
contact_us: "Contact us",
connect: "Connect",
disconnect: "Disconnect",
start: "Start",
stop: "Stop"
};
stop: "Stop",
}

View File

@@ -78,8 +78,7 @@ export default {
enabled: "Activé",
disabled: "Désactivé",
proxy: "Proxy",
postwoman_official_proxy_hosting:
"Le proxy officiel de Postwoman est hébergé par Apollo Software.",
postwoman_official_proxy_hosting: "Le proxy officiel de Postwoman est hébergé par Apollo Software.",
read_the: "Lire la",
apollosw_privacy_policy: "politique de confidentialité Apollo Software",
contact_us: "Contactez nous",
@@ -235,8 +234,7 @@ export default {
token_request_saved: "Requête de Token sauvegardé",
donate_info1:
"Si vous avez apprécié la productivité de l'utilisation de Postwoman, considérez le don comme un signe d'appréciation.",
donate_info2:
"Vous pouvez soutenir le développement de Postwoman par les méthodes suivantes :",
donate_info2: "Vous pouvez soutenir le développement de Postwoman par les méthodes suivantes :",
one_time_recurring: "Ponctuel ou récurrent",
one_time: "Une fois",
recurring: "Récurrent",
@@ -260,5 +258,5 @@ export default {
turn_on: "Activer",
login_first: "Se connecter d'abord",
paste_a_collection: "Coller une collection",
import_from_sync: "Importer depuis la synchronisation"
};
import_from_sync: "Importer depuis la synchronisation",
}

View File

@@ -78,13 +78,12 @@ export default {
enabled: "diaktifkan",
disabled: "dinonaktifkan",
proxy: "Proksi",
postwoman_official_proxy_hosting:
"Proksi Resmi Postwoman dalam penginangan Apollo Software.",
postwoman_official_proxy_hosting: "Proksi Resmi Postwoman dalam penginangan Apollo Software.",
read_the: "Bacalah",
apollosw_privacy_policy: "kebijakan privasi Apollo Software",
contact_us: "Hubungi kami",
connect: "Menghubungkan",
disconnect: "Memutuskan",
start: "Mulai",
stop: "Berhenti"
};
stop: "Berhenti",
}

View File

@@ -78,8 +78,7 @@ export default {
enabled: "有効",
disabled: "無効",
proxy: "プロキシ",
postwoman_official_proxy_hosting:
"Postwomanの公式プロキシは、Apollo TVがホストしています。",
postwoman_official_proxy_hosting: "Postwomanの公式プロキシは、Apollo Softwareがホストしています。",
read_the: "プライバシーポリシー",
apollosw_privacy_policy: "を読む",
contact_us: "お問い合わせ",
@@ -231,7 +230,8 @@ export default {
enable_proxy: "プロキシを有効にしてみてください",
complete_config_urls: "設定URLsを入力してください",
token_request_saved: "トークンリクエストを保存した",
donate_info1: "Postwomanを非常に役に立つと思われる場合、感謝の印として寄付のご検討をお願いします。",
donate_info1:
"Postwomanを非常に役に立つと思われる場合、感謝の印として寄付のご検討をお願いします。",
donate_info2: "以下の方法でPostwomanの開発をサポートできます",
one_time_recurring: "一度又は定期的",
one_time: "一度",

264
lang/ko-KR.js Normal file
View File

@@ -0,0 +1,264 @@
export default {
home: "홈",
realtime: "실시간",
graphql: "GraphQL",
settings: "설정",
request: "Request",
install_pwa: "PWA 설치",
support_us: "도와주기",
tweet: "트윗",
options: "옵션",
communication: "통신",
endpoint: "Endpoint",
schema: "Schema",
theme: "테마",
subscribe: "구독",
choose_language: "언어 선택",
shortcuts: "단축키",
send_request: "Send Request",
save_to_collections: "Save to Collections",
copy_request_link: "Copy Request Link",
reset_request: "Reset Request",
support_us_on: "Support us on",
open_collective: "Open Collective",
paypal: "PayPal",
patreon: "Patreon",
javascript_code: "JavaScript 코드",
method: "Method",
path: "Path",
label: "Label",
again: "Again",
content_type: "Content Type",
raw_input: "Raw input",
parameter_list: "Parameter List",
raw_request_body: "Raw Request Body",
show_code: "Show Code",
hide_code: "Hide Code",
show_prerequest_script: "Show Pre-Request Script",
hide_prerequest_script: "Hide Pre-Request Script",
authentication: "Authentication",
authentication_type: "Authentication type",
include_in_url: "Include in URL",
parameters: "Parameters",
expand_response: "Expand response",
collapse_response: "Collapse response",
hide_preview: "Hide Preview",
preview_html: "Preview HTML",
history: "히스토리",
collections: "Collections",
import_curl: "Import cURL",
import: "가져오기",
generate_code: "Generate code",
request_type: "Request type",
generated_code: "Generated code",
status: "상태",
headers: "Headers",
websocket: "WebSocket",
waiting_for_connection: "(waiting for connection)",
message: "Message",
sse: "SSE",
server: "Server",
events: "Events",
url: "URL",
get_schema: "Get schema",
header_list: "Header list",
add_new: "추가",
response: "Response",
query: "Query",
queries: "Queries",
query_variables: "Variables",
mutations: "Mutations",
subscriptions: "Subscriptions",
types: "Types",
send: "Send",
background: "배경",
color: "색상",
labels: "Labels",
multi_color: "Multi-color",
enabled: "허용",
disabled: "허용안함",
proxy: "Proxy",
postwoman_official_proxy_hosting:
"Postwoman의 공식적인 Proxy는 ApolloTV가 호스트 합니다.",
read_the: "Read the",
apollotv_privacy_policy: "ApolloTV 개인정보 처리방침",
contact_us: "문의 하기",
connect: "연결",
disconnect: "연결 중지",
start: "시작",
stop: "중지",
access_token: "Access Token",
token_list: "Token List",
get_token: "Get New Token",
manage_token: "Manage Access Token",
save_token: "Save Access Token",
use_token: "Use Saved Token",
request_token: "Request Token",
save_token_req: "Save Token Request",
manage_token_req: "Manage Token Request",
use_token_req: "Use Token Request",
token_req_name: "Request Name",
token_req_details: "Request Details",
token_name: "Token Name",
oidc_discovery_url: "OIDC Discovery URL",
auth_url: "Auth URL",
access_token_url: "Access Token URL",
client_id: "Client ID",
scope: "Scope",
state: "State",
token_req_list: "Token Request List",
no_path: "No path",
no_label: "No label",
prerequest_script: "Pre-Request Script",
no_prerequest_script: "No pre-request script",
search_history: "히스토리 검색",
history_empty: "히스토리가 비었습니다",
history_deleted: "History Deleted",
clear: "비우기",
clear_all: "모두 비우기",
cleared: "Cleared",
close: "Close",
sort: "정렬",
time: "Time",
duration: "Duration",
no_duration: "No duration",
show_more: "Show more",
hide_more: "Hide more",
collection: "Collection",
current_collection: "Current Collection",
select_collection: "Select a Collection",
create_collection: "Create a Collection",
new: "New",
import_export: "Import / Export",
more: "더보기",
folder: "Folder",
new_folder: "New Folder",
my_new_folder: "My New Folder",
folder_empty: "Folder is empty",
edit_folder: "Edit Folder",
edit: "Edit",
delete: "Delete",
deleted: "Deleted",
undo: "되돌리기",
collection_empty: "Collection is empty",
new_collection: "New Collection",
my_new_collection: "My New Collection",
edit_collection: "Edit Collection",
edit_request: "Edit Request",
save_request_as: "Save Request As",
export: "내보내기",
connecting_to: "Connecting to {name}...",
connected: "연결됨",
connected_to: "{name} (으)로 연결됨",
disconnected: "연결 해제",
disconnected_from: "Disconnected from {name}",
something_went_wrong: "Something went wrong!",
error_occurred: "An error has occurred.",
browser_support_sse:
"This browser doesn't seems to have Server Sent Events support.",
log: "Log",
no_url: "No URL",
run_query: "Run Query",
copy_query: "Copy Query",
kinda_dark: "Kinda Dark",
clearly_white: "Clearly White",
just_black: "Just Black",
auto_system: "Auth (system)",
green: "Green",
yellow: "노랑",
pink: "핑크",
red: "빨강",
purple: "보라",
orange: "오렌지",
cyan: "청록",
blue: "파랑",
loading: "로딩...",
fetching: "Fetching...",
waiting_send_req: "(waiting to send request)",
cancel: "취소",
save: "저장",
dismiss: "무시",
are_you_sure: "Are you sure?",
yes: "예",
no: "아니오",
restore: "복구",
add_star: "Add star",
remove_star: "Remove star",
nothing_found: "Nothing found",
replace_current: "Replace current",
replace_json: "Replace with JSON",
preserve_current: "Preserve current",
import_json: "Import from JSON",
download_file: "파일 다운로드",
upload_file: "파일 업로드",
copy_response: "Copy response",
copy_code: "Copy code",
copy_schema: "Copy Schema",
use_request: "Use request",
documentation: "문서화",
docs: "Docs",
reset_default: "기본값으로 재설정",
fields: "FIELDS",
deprecated: "DEPRECATED",
add_one_header: "(add at least one header)",
add_one_parameter: "(add at least one parameter)",
header_count: "header {count}",
parameter_count: "parameter {count}",
variable_count: "variable {count}",
value_count: "value {count}",
send_request_first: "Send a request first",
generate_docs: "Generate Documentation",
generate_docs_message:
"Import any Postwoman Collection to Generate Documentation on-the-go.",
generate_docs_first: "Generate documentation first",
docs_generated: "Documentation generated",
import_collections: "Import collections",
optional: "(optional)",
json: "JSON",
none: "None",
username: "사용자명",
password: "암호",
token: "Token",
payload: "Payload",
choose_file: "파일 선택",
file_imported: "File imported",
f12_details: "(F12 for details)",
we_use_cookies: "우리는 쿠키를 사용합니다",
copied_to_clipboard: "Copied to clipboard",
finished_in: "Finished in {duration}ms",
check_console_details: "Check console for details.",
download_started: "Download started",
url_invalid_format: "URL is not formatted properly",
curl_invalid_format: "cURL is not formatted properly",
enable_proxy: "Try enabling Proxy",
complete_config_urls: "Please complete configuration urls.",
token_request_saved: "Token request saved",
donate_info1:
"If you have enjoyed the productivity of using Postwoman, consider donating as a sign of appreciation.",
donate_info2:
"You can support Postwoman development via the following methods:",
one_time_recurring: "One-time or recurring",
one_time: "One-time",
recurring: "Recurring",
wiki: "위키",
error: "오류",
go_home: "Go Home",
reload: "새로고침",
enter_curl: "Enter cURL",
empty: "Empty",
extensions: "확장 프로그램",
extensions_info1: "Postwoman의 액세스를 단순화 하는 브라우저 확장 프로그램",
extensions_info2: "Postwoman 확장 프로그램을 설치해보세요!",
installed: "설치함",
login_with: "Login with",
logged_out: "Logged out",
logout: "로그아웃",
account: "계정",
sync: "동기화",
syncHistory: "History",
syncCollections: "Collections",
turn_on: "Turn on",
login_first: "Login first",
paste_a_collection: "Paste a Collection",
import_from_sync: "Import from Sync"
};

View File

@@ -85,5 +85,5 @@ export default {
connect: "Conectar",
disconnect: "Desconectar",
start: "Começar",
stop: "Pare"
};
stop: "Pare",
}

View File

@@ -78,13 +78,12 @@ export default {
enabled: "Aktif",
disabled: "Aktif değil",
proxy: "Proxy",
postwoman_official_proxy_hosting:
"Proxy Oficial de Postwoman está hospedado en Apollo Software.",
postwoman_official_proxy_hosting: "Proxy Oficial de Postwoman está hospedado en Apollo Software.",
read_the: "Leer la",
apollosw_privacy_policy: "Apollo Software gizlilik politikaları",
contact_us: "Bizimle iletişime geçin",
connect: "Bağlan",
disconnect: "Kesmek",
start: "Başla",
stop: "Durdurmak"
};
stop: "Durdurmak",
}

View File

@@ -85,5 +85,5 @@ export default {
connect: "连接",
disconnect: "断开",
start: "开始",
stop: "停止"
};
stop: "停止",
}

View File

@@ -304,11 +304,9 @@
<button
class="icon"
v-tooltip="
(fb.currentUser.displayName ||
'<label><i>Name not found</i></label>') +
(fb.currentUser.displayName || '<label><i>Name not found</i></label>') +
'<br>' +
(fb.currentUser.email ||
'<label><i>Email not found</i></label>')
(fb.currentUser.email || '<label><i>Email not found</i></label>')
"
aria-label="Account"
>
@@ -346,31 +344,19 @@
</button>
<template slot="popover">
<div>
<button
class="icon"
@click="showExtensions = true"
v-close-popover
>
<button class="icon" @click="showExtensions = true" v-close-popover>
<i class="material-icons">extension</i>
<span>{{ $t("extensions") }}</span>
</button>
</div>
<div>
<button
class="icon"
@click="showShortcuts = true"
v-close-popover
>
<button class="icon" @click="showShortcuts = true" v-close-popover>
<i class="material-icons">keyboard</i>
<span>{{ $t("shortcuts") }}</span>
</button>
</div>
<div>
<button
class="icon"
@click="showSupport = true"
v-close-popover
>
<button class="icon" @click="showSupport = true" v-close-popover>
<i class="material-icons">favorite</i>
<span>{{ $t("support_us") }}</span>
</button>
@@ -414,10 +400,7 @@
<span v-if="version.name" class="mono">
<a
class="footer-link"
:href="
'https://github.com/liyasthomas/postwoman/releases/tag/' +
version.name
"
:href="'https://github.com/liyasthomas/postwoman/releases/tag/' + version.name"
target="_blank"
rel="noopener"
v-tooltip="'GitHub'"
@@ -443,20 +426,12 @@
<!-- <span v-if="version.variant">({{version.variant}})</span> -->
</span>
<span>
<a
href="https://liyasthomas.web.app"
target="_blank"
rel="noopener"
>
<a href="https://liyasthomas.web.app" target="_blank" rel="noopener">
<button class="icon" v-tooltip="'Liyas Thomas'">
🦄
</button>
</a>
<a
href="mailto:liyascthomas@gmail.com"
target="_blank"
rel="noopener"
>
<a href="mailto:liyascthomas@gmail.com" target="_blank" rel="noopener">
<button class="icon" v-tooltip="$t('contact_us')">
<i class="material-icons">email</i>
</button>
@@ -520,11 +495,7 @@
/>
</svg>
<span>Firefox</span>
<span
class="icon"
v-if="firefoxExtInstalled"
v-tooltip="$t('installed')"
>
<span class="icon" v-if="firefoxExtInstalled" v-tooltip="$t('installed')">
<i class="material-icons">done</i>
</span>
</button>
@@ -549,11 +520,7 @@
/>
</svg>
<span>Chrome</span>
<span
class="icon"
v-if="chromeExtInstalled"
v-tooltip="$t('installed')"
>
<span class="icon" v-if="chromeExtInstalled" v-tooltip="$t('installed')">
<i class="material-icons">done</i>
</span>
</button>
@@ -671,42 +638,42 @@
</style>
<script>
import intializePwa from "../assets/js/pwa";
import * as version from "../.postwoman/version.json";
import { hasExtensionInstalled } from "../functions/strategies/ExtensionStrategy";
import firebase from "firebase/app";
import { fb } from "../functions/fb";
import intializePwa from "../assets/js/pwa"
import * as version from "../.postwoman/version.json"
import { hasExtensionInstalled } from "../functions/strategies/ExtensionStrategy"
import firebase from "firebase/app"
import { fb } from "../functions/fb"
export default {
components: {
logo: () => import("../components/logo"),
modal: () => import("../components/modal"),
login: () => import("../components/firebase/login")
login: () => import("../components/firebase/login"),
},
methods: {
getSpecialKey() {
return /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform) ? "⌘" : "Ctrl";
return /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform) ? "⌘" : "Ctrl"
},
linkActive(path) {
return {
"nuxt-link-exact-active": this.$route.path === path,
"nuxt-link-active": this.$route.path === path
};
"nuxt-link-active": this.$route.path === path,
}
},
logout() {
fb.currentUser = null;
fb.currentUser = null
firebase
.auth()
.signOut()
.catch(err => {
this.$toast.show(err.message || err, {
icon: "error"
});
});
icon: "error",
})
})
this.$toast.info(this.$t("logged_out"), {
icon: "vpn_key"
});
icon: "vpn_key",
})
},
nativeShare() {
if (navigator.share) {
@@ -715,14 +682,14 @@ export default {
title: "Postwoman",
text:
"Postwoman • A free, fast and beautiful API request builder - Web alternative to Postman - Helps you create requests faster, saving precious time on development.",
url: "https://postwoman.io/"
url: "https://postwoman.io/",
})
.then(() => {})
.catch(console.error);
.catch(console.error)
} else {
// fallback
}
}
},
},
data() {
@@ -737,48 +704,43 @@ export default {
showSupport: false,
extensionInstalled: hasExtensionInstalled(),
fb,
navigatorShare: navigator.share
};
navigatorShare: navigator.share,
}
},
beforeMount() {
// Set version data
this.version = version.default;
this.version = version.default
// Load theme settings
(() => {
;(() => {
// Apply theme from settings.
document.documentElement.className =
this.$store.state.postwoman.settings.THEME_CLASS || "";
document.documentElement.className = this.$store.state.postwoman.settings.THEME_CLASS || ""
// Load theme color data from settings, or use default color.
let color = this.$store.state.postwoman.settings.THEME_COLOR || "#50fa7b";
let vibrant =
this.$store.state.postwoman.settings.THEME_COLOR_VIBRANT || true;
document.documentElement.style.setProperty("--ac-color", color);
let color = this.$store.state.postwoman.settings.THEME_COLOR || "#50fa7b"
let vibrant = this.$store.state.postwoman.settings.THEME_COLOR_VIBRANT || true
document.documentElement.style.setProperty("--ac-color", color)
document.documentElement.style.setProperty(
"--act-color",
vibrant ? "rgba(32, 33, 36, 1)" : "rgba(255, 255, 255, 1)"
);
})();
)
})()
},
mounted() {
if (process.client) {
document.body.classList.add("afterLoad");
document.body.classList.add("afterLoad")
}
document
.querySelector("meta[name=theme-color]")
.setAttribute(
"content",
this.$store.state.postwoman.settings.THEME_TAB_COLOR || "#202124"
);
.setAttribute("content", this.$store.state.postwoman.settings.THEME_TAB_COLOR || "#202124")
// Initializes the PWA code - checks if the app is installed,
// etc.
(async () => {
this.showInstallPrompt = await intializePwa();
let cookiesAllowed = localStorage.getItem("cookiesAllowed") === "yes";
;(async () => {
this.showInstallPrompt = await intializePwa()
let cookiesAllowed = localStorage.getItem("cookiesAllowed") === "yes"
if (!cookiesAllowed) {
this.$toast.show(this.$t("we_use_cookies"), {
icon: "info",
@@ -788,19 +750,19 @@ export default {
{
text: this.$t("dismiss"),
onClick: (e, toastObject) => {
localStorage.setItem("cookiesAllowed", "yes");
toastObject.goAway(0);
}
}
]
});
localStorage.setItem("cookiesAllowed", "yes")
toastObject.goAway(0)
},
},
],
})
}
let showExtensionsToast =
localStorage.getItem("showExtensionsToast") === "yes";
if (
!this.extensionInstalled &&
!showExtensionsToast
) {
let showExtensionsToast = localStorage.getItem("showExtensionsToast") === "yes"
// Just return if showExtensionsToast is "no"
if (!showExtensionsToast) return
if (!this.extensionInstalled) {
setTimeout(() => {
this.$toast.show(this.$t("extensions_info2"), {
icon: "extension",
@@ -810,73 +772,78 @@ export default {
{
text: this.$t("yes"),
onClick: (e, toastObject) => {
this.showExtensions = true;
localStorage.setItem("showExtensionsToast", "yes");
toastObject.goAway(0);
}
this.showExtensions = true
localStorage.setItem("showExtensionsToast", "yes")
toastObject.goAway(0)
},
},
{
text: this.$t("no"),
onClick: (e, toastObject) => {
toastObject.goAway(0);
}
}
]
});
}, 15000);
this.$store.commit("setMiscState", {
value: false,
attribute: "showExtensionsToast",
})
localStorage.setItem("showExtensionsToast", "no")
toastObject.goAway(0)
},
},
],
})
}, 15000)
}
this._keyListener = function(e) {
if (e.key === "Escape") {
e.preventDefault();
this.showExtensions = this.showShortcuts = this.showSupport = false;
e.preventDefault()
this.showExtensions = this.showShortcuts = this.showSupport = false
}
};
document.addEventListener("keydown", this._keyListener.bind(this));
})();
}
document.addEventListener("keydown", this._keyListener.bind(this))
})()
window.addEventListener("scroll", event => {
let mainNavLinks = document.querySelectorAll("nav ul li a");
let fromTop = window.scrollY;
let mainNavLinks = document.querySelectorAll("nav ul li a")
let fromTop = window.scrollY
mainNavLinks.forEach(link => {
let section = document.querySelector(link.hash);
let section = document.querySelector(link.hash)
if (
section &&
section.offsetTop <= fromTop &&
section.offsetTop + section.offsetHeight > fromTop
) {
link.classList.add("current");
link.classList.add("current")
} else {
link.classList.remove("current");
link.classList.remove("current")
}
});
});
})
})
console.log(
"%cWe ❤︎ open source!",
"background-color:white;padding:8px 16px;border-radius:8px;font-size:32px;color:red;"
);
)
console.log(
"%cContribute: https://github.com/liyasthomas/postwoman",
"background-color:black;padding:4px 8px;border-radius:8px;font-size:16px;color:white;"
);
)
},
watch: {
$route() {
// this.$toast.clear();
}
},
},
computed: {
availableLocales() {
return this.$i18n.locales.filter(i => i.code !== this.$i18n.locale);
}
return this.$i18n.locales.filter(i => i.code !== this.$i18n.locale)
},
},
beforeDestroy() {
document.removeEventListener("keydown", this._keyListener);
}
};
document.removeEventListener("keydown", this._keyListener)
},
}
</script>

View File

@@ -1,10 +1,6 @@
<template>
<div class="page page-error">
<img
src="~static/icons/error.svg"
:alt="$t('error')"
class="error_banner"
/>
<img src="~static/icons/error.svg" :alt="$t('error')" class="error_banner" />
<h2>{{ error.statusCode }}</h2>
<h3>{{ error.message }}</h3>
<p>
@@ -39,16 +35,16 @@ export default {
methods: {
reloadApplication() {
this.$router.push("/", () => window.location.reload());
}
this.$router.push("/", () => window.location.reload())
},
},
head() {
return {
bodyAttrs: {
class: "sticky-footer"
}
};
}
};
class: "sticky-footer",
},
}
},
}
</script>

View File

@@ -1,5 +1,5 @@
export default function({ route, redirect }) {
if (route.fullPath !== "/") {
return redirect("/");
return redirect("/")
}
}

View File

@@ -4,201 +4,201 @@ export const meta = {
name: "Postwoman",
shortDescription: "A free, fast and beautiful API request builder",
description:
"Web alternative to Postman - Helps you create requests faster, saving precious time on development."
};
"Web alternative to Postman - Helps you create requests faster, saving precious time on development.",
}
// Sets the base path for the router.
// Important for deploying to GitHub pages.
// -- Travis includes the author in the repo slug,
// so if there's a /, we need to get everything after it.
let repoName = (process.env.TRAVIS_REPO_SLUG || "").split("/").pop();
let repoName = (process.env.TRAVIS_REPO_SLUG || "").split("/").pop()
export const routerBase =
process.env.DEPLOY_ENV === "GH_PAGES"
? {
router: {
base: `/${repoName}/`
}
base: `/${repoName}/`,
},
}
: {
router: {
base: "/"
}
};
base: "/",
},
}
export default {
mode: "spa",
/*
** Headers of the page
*/
server: {
host: "0.0.0.0" // default: localhost
host: "0.0.0.0", // default: localhost
},
render: {
bundleRenderer: {
shouldPreload: (file, type) => {
return ["script", "style", "font"].includes(type);
}
}
return ["script", "style", "font"].includes(type)
},
},
},
head: {
title: `${meta.name} \u2022 ${meta.shortDescription}`,
meta: [
{
charset: "utf-8"
charset: "utf-8",
},
{
name: "viewport",
content:
"width=device-width, initial-scale=1, minimum-scale=1, viewport-fit=cover, minimal-ui"
"width=device-width, initial-scale=1, minimum-scale=1, viewport-fit=cover, minimal-ui",
},
{
hid: "description",
name: "description",
content: meta.description || ""
content: meta.description || "",
},
{
name: "keywords",
content:
"postwoman, postwoman chrome, postwoman online, postwoman for mac, postwoman app, postwoman for windows, postwoman google chrome, postwoman chrome app, get postwoman, postwoman web, postwoman android, postwoman app for chrome, postwoman mobile app, postwoman web app, api, request, testing, tool, rest, websocket, sse, graphql"
"postwoman, postwoman chrome, postwoman online, postwoman for mac, postwoman app, postwoman for windows, postwoman google chrome, postwoman chrome app, get postwoman, postwoman web, postwoman android, postwoman app for chrome, postwoman mobile app, postwoman web app, api, request, testing, tool, rest, websocket, sse, graphql",
},
{
name: "X-UA-Compatible",
content: "IE=edge, chrome=1"
content: "IE=edge, chrome=1",
},
{
itemprop: "name",
content: `${meta.name} \u2022 ${meta.shortDescription}`
content: `${meta.name} \u2022 ${meta.shortDescription}`,
},
{
itemprop: "description",
content: meta.description
content: meta.description,
},
{
itemprop: "image",
content: `https://postwoman.io/logo.jpg`
content: `https://postwoman.io/logo.jpg`,
},
// Add to homescreen for Chrome on Android. Fallback for PWA (handled by nuxt)
{
name: "application-name",
content: meta.name
content: meta.name,
},
// Add to homescreen for Safari on iOS
{
name: "apple-mobile-web-app-capable",
content: "yes"
content: "yes",
},
{
name: "apple-mobile-web-app-status-bar-style",
content: "black-translucent"
content: "black-translucent",
},
{
name: "apple-mobile-web-app-title",
content: meta.name
content: meta.name,
},
// Windows phone tile icon
{
name: "msapplication-TileImage",
content: `${routerBase.router.base}icons/icon-144x144.png`
content: `${routerBase.router.base}icons/icon-144x144.png`,
},
{
name: "msapplication-TileColor",
content: "#202124"
content: "#202124",
},
{
name: "msapplication-tap-highlight",
content: "no"
content: "no",
},
// OpenGraph
{
property: "og:site_name",
content: meta.name
content: meta.name,
},
{
property: "og:url",
content: "https://postwoman.io"
content: "https://postwoman.io",
},
{
property: "og:type",
content: "website"
content: "website",
},
{
property: "og:title",
content: `${meta.name} \u2022 ${meta.shortDescription}`
content: `${meta.name} \u2022 ${meta.shortDescription}`,
},
{
property: "og:description",
content: meta.description
content: meta.description,
},
{
property: "og:image",
content: `https://postwoman.io/logo.jpg`
content: `https://postwoman.io/logo.jpg`,
},
// Twitter
{
name: "twitter:card",
content: "summary_large_image"
content: "summary_large_image",
},
{
name: "twitter:site",
content: "@liyasthomas"
content: "@liyasthomas",
},
{
name: "twitter:creator",
content: "@liyasthomas"
content: "@liyasthomas",
},
{
name: "twitter:url",
content: "https://postwoman.io"
content: "https://postwoman.io",
},
{
name: "twitter:title",
content: `${meta.name} \u2022 ${meta.shortDescription}`
content: `${meta.name} \u2022 ${meta.shortDescription}`,
},
{
name: "twitter:description",
content: meta.description
content: meta.description,
},
{
name: "twitter:image",
content: "https://postwoman.io/logo.jpg"
}
content: "https://postwoman.io/logo.jpg",
},
],
link: [
{
rel: "icon",
type: "image/x-icon",
href: `${routerBase.router.base}favicon.ico`
href: `${routerBase.router.base}favicon.ico`,
},
// Home-screen icons (iOS)
{
rel: "apple-touch-icon",
href: `${routerBase.router.base}icons/icon-48x48.png`
href: `${routerBase.router.base}icons/icon-48x48.png`,
},
{
rel: "apple-touch-icon",
sizes: "72x72",
href: `${routerBase.router.base}icons/icon-72x72.png`
href: `${routerBase.router.base}icons/icon-72x72.png`,
},
{
rel: "apple-touch-icon",
sizes: "96x96",
href: `${routerBase.router.base}icons/icon-96x96.png`
href: `${routerBase.router.base}icons/icon-96x96.png`,
},
{
rel: "apple-touch-icon",
sizes: "144x144",
href: `${routerBase.router.base}icons/icon-144x144.png`
href: `${routerBase.router.base}icons/icon-144x144.png`,
},
{
rel: "apple-touch-icon",
sizes: "192x192",
href: `${routerBase.router.base}icons/icon-192x192.png`
}
]
href: `${routerBase.router.base}icons/icon-192x192.png`,
},
],
},
/*
** Customize the progress-bar color
*/
loading: {
color: "var(--ac-color)"
color: "var(--ac-color)",
},
/*
** Customize the loading indicator
@@ -206,26 +206,22 @@ export default {
loadingIndicator: {
name: "pulse",
color: "var(--ac-color)",
background: "var(--bg-color)"
background: "var(--bg-color)",
},
/*
** Global CSS
*/
css: [
"~/assets/css/styles.scss",
"~/assets/css/themes.scss",
"~/assets/css/fonts.scss"
],
css: ["~/assets/css/styles.scss", "~/assets/css/themes.scss", "~/assets/css/fonts.scss"],
/*
** Plugins to load before mounting the App
*/
plugins: [
{
src: "~/plugins/vuex-persist"
src: "~/plugins/vuex-persist",
},
{
src: "~/plugins/v-tooltip"
}
src: "~/plugins/v-tooltip",
},
],
/*
** Nuxt.js dev-modules
@@ -244,11 +240,11 @@ export default {
[
"@nuxtjs/google-tag-manager",
{
id: process.env.GTM_ID || "GTM-MXWD8NQ"
}
id: process.env.GTM_ID || "GTM-MXWD8NQ",
},
],
["@nuxtjs/robots"],
["nuxt-i18n"]
["nuxt-i18n"],
],
pwa: {
manifest: {
@@ -259,43 +255,43 @@ export default {
theme_color: "#202124",
background_color: "#202124",
start_url: `${routerBase.router.base}`
start_url: `${routerBase.router.base}`,
},
meta: {
description: meta.shortDescription,
theme_color: "#202124"
theme_color: "#202124",
},
icons: (sizes => {
let icons = [];
let icons = []
for (let size of sizes) {
icons.push({
src: `${routerBase.router.base}icons/icon-${size}x${size}.png`,
type: "image/png",
sizes: `${size}x${size}`
});
sizes: `${size}x${size}`,
})
}
return icons;
})([48, 72, 96, 144, 192, 512])
return icons
})([48, 72, 96, 144, 192, 512]),
},
toast: {
position: "bottom-center",
duration: 3000,
theme: "bubble",
keepOnHover: true
keepOnHover: true,
},
googleAnalytics: {
id: process.env.GA_ID || "UA-61422507-2"
id: process.env.GA_ID || "UA-61422507-2",
},
sitemap: {
hostname: "https://postwoman.io"
hostname: "https://postwoman.io",
},
robots: {
UserAgent: "*",
Disallow: "",
Allow: "/",
Sitemap: "https://postwoman.io/sitemap.xml"
Sitemap: "https://postwoman.io/sitemap.xml",
},
i18n: {
locales: [
@@ -303,69 +299,75 @@ export default {
code: "en",
name: "English",
iso: "en-US",
file: "en-US.js"
file: "en-US.js",
},
{
code: "es",
name: "Español",
iso: "es-ES",
file: "es-ES.js"
file: "es-ES.js",
},
{
code: "fr",
name: "Français",
iso: "fr-FR",
file: "fr-FR.js"
file: "fr-FR.js",
},
{
code: "fa",
name: "Farsi",
iso: "fa-IR",
file: "fa-IR.js"
file: "fa-IR.js",
},
{
code: "pt",
name: "Português Brasileiro",
iso: "pt-BR",
file: "pt-BR.js"
file: "pt-BR.js",
},
{
code: "cn",
name: "简体中文",
iso: "zh-CN",
file: "zh-CN.js"
file: "zh-CN.js",
},
{
code: "id",
name: "Bahasa Indonesia",
iso: "id-ID",
file: "id-ID.js"
file: "id-ID.js",
},
{
code: "tr",
name: "Türkçe",
iso: "tr-TR",
file: "tr-TR.js"
file: "tr-TR.js",
},
{
code: "de",
name: "Deutsch",
iso: "de-DE",
file: "de-DE.js"
file: "de-DE.js",
},
{
code: "ja",
name: "日本語",
iso: "ja-JP",
file: "ja-JP.js"
},
{
code: "ko",
name: "한국어",
iso: "ko-KR",
file: "ko-KR.js"
}
],
defaultLocale: "en",
vueI18n: {
fallbackLocale: "en"
fallbackLocale: "en",
},
lazy: true,
langDir: "lang/"
langDir: "lang/",
},
/*
** Build configuration
@@ -374,16 +376,16 @@ export default {
/*
** You can extend webpack config here
*/
extend(config, ctx) {}
extend(config, ctx) {},
},
/*
** Generate configuration
*/
generate: {
fallback: true
fallback: true,
},
/*
** Router configuration
*/
...routerBase
};
...routerBase,
}

663
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "postwoman",
"version": "1.8.0",
"version": "1.9.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -1860,11 +1860,23 @@
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
"integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w=="
},
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
"integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
"dev": true
},
"@types/node": {
"version": "12.12.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.17.tgz",
"integrity": "sha512-Is+l3mcHvs47sKy+afn2O1rV4ldZFU7W8101cNlOd+MRbjM4Onida8jSZnJdTe/0Pcf25g9BNIUsuugmE6puHA=="
},
"@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
"dev": true
},
"@types/q": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz",
@@ -2378,6 +2390,12 @@
"resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
"integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ="
},
"array-differ": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz",
"integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==",
"dev": true
},
"array-find-index": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
@@ -2389,11 +2407,23 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"array-union": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
"dev": true
},
"array-unique": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
},
"arrify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
"integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
"dev": true
},
"ascli": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz",
@@ -3565,6 +3595,12 @@
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs="
},
"compare-versions": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz",
"integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==",
"dev": true
},
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
@@ -4293,6 +4329,12 @@
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
},
"dedent": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
"integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=",
"dev": true
},
"deepmerge": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
@@ -5214,6 +5256,15 @@
"locate-path": "^3.0.0"
}
},
"find-versions": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz",
"integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==",
"dev": true,
"requires": {
"semver-regex": "^2.0.0"
}
},
"firebase": {
"version": "7.9.1",
"resolved": "https://registry.npmjs.org/firebase/-/firebase-7.9.1.tgz",
@@ -5514,6 +5565,12 @@
"integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
"dev": true
},
"get-own-enumerable-property-symbols": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
"integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==",
"dev": true
},
"get-stdin": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
@@ -6519,6 +6576,132 @@
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
"integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="
},
"husky": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/husky/-/husky-4.2.3.tgz",
"integrity": "sha512-VxTsSTRwYveKXN4SaH1/FefRJYCtx+wx04sSVcOpD7N2zjoHxa+cEJ07Qg5NmV3HAK+IRKOyNVpi2YBIVccIfQ==",
"dev": true,
"requires": {
"chalk": "^3.0.0",
"ci-info": "^2.0.0",
"compare-versions": "^3.5.1",
"cosmiconfig": "^6.0.0",
"find-versions": "^3.2.0",
"opencollective-postinstall": "^2.0.2",
"pkg-dir": "^4.2.0",
"please-upgrade-node": "^3.2.0",
"slash": "^3.0.0",
"which-pm-runs": "^1.0.0"
},
"dependencies": {
"chalk": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"ci-info": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
"integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
"dev": true
},
"cosmiconfig": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
"integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
"dev": true,
"requires": {
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.1.0",
"parse-json": "^5.0.0",
"path-type": "^4.0.0",
"yaml": "^1.7.2"
}
},
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dev": true,
"requires": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
}
},
"import-fresh": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
"integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
"dev": true,
"requires": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
}
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dev": true,
"requires": {
"p-locate": "^4.1.0"
}
},
"p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dev": true,
"requires": {
"p-limit": "^2.2.0"
}
},
"parse-json": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz",
"integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1",
"lines-and-columns": "^1.1.6"
}
},
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true
},
"path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"dev": true
},
"pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
"dev": true,
"requires": {
"find-up": "^4.0.0"
}
},
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true
}
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -6884,6 +7067,12 @@
"has": "^1.0.3"
}
},
"is-regexp": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz",
"integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=",
"dev": true
},
"is-resolvable": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
@@ -7148,6 +7337,239 @@
"invert-kv": "^1.0.0"
}
},
"lines-and-columns": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
"dev": true
},
"lint-staged": {
"version": "10.0.8",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.0.8.tgz",
"integrity": "sha512-Oa9eS4DJqvQMVdywXfEor6F4vP+21fPHF8LUXgBbVWUSWBddjqsvO6Bv1LwMChmgQZZqwUvgJSHlu8HFHAPZmA==",
"dev": true,
"requires": {
"chalk": "^3.0.0",
"commander": "^4.0.1",
"cosmiconfig": "^6.0.0",
"debug": "^4.1.1",
"dedent": "^0.7.0",
"execa": "^3.4.0",
"listr": "^0.14.3",
"log-symbols": "^3.0.0",
"micromatch": "^4.0.2",
"normalize-path": "^3.0.0",
"please-upgrade-node": "^3.2.0",
"string-argv": "0.3.1",
"stringify-object": "^3.3.0"
},
"dependencies": {
"braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"requires": {
"fill-range": "^7.0.1"
}
},
"chalk": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
"dev": true
},
"cosmiconfig": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
"integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
"dev": true,
"requires": {
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.1.0",
"parse-json": "^5.0.0",
"path-type": "^4.0.0",
"yaml": "^1.7.2"
}
},
"cross-spawn": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz",
"integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==",
"dev": true,
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
}
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"execa": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz",
"integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==",
"dev": true,
"requires": {
"cross-spawn": "^7.0.0",
"get-stream": "^5.0.0",
"human-signals": "^1.1.1",
"is-stream": "^2.0.0",
"merge-stream": "^2.0.0",
"npm-run-path": "^4.0.0",
"onetime": "^5.1.0",
"p-finally": "^2.0.0",
"signal-exit": "^3.0.2",
"strip-final-newline": "^2.0.0"
}
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"requires": {
"to-regex-range": "^5.0.1"
}
},
"get-stream": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
"integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
"dev": true,
"requires": {
"pump": "^3.0.0"
}
},
"import-fresh": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
"integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
"dev": true,
"requires": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
}
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true
},
"is-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
"integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
"dev": true
},
"micromatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
"integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
"dev": true,
"requires": {
"braces": "^3.0.1",
"picomatch": "^2.0.5"
}
},
"npm-run-path": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
"dev": true,
"requires": {
"path-key": "^3.0.0"
}
},
"p-finally": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz",
"integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==",
"dev": true
},
"parse-json": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz",
"integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1",
"lines-and-columns": "^1.1.6"
}
},
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true
},
"path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"dev": true
},
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true
},
"shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"requires": {
"shebang-regex": "^3.0.0"
}
},
"shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"requires": {
"is-number": "^7.0.0"
}
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"requires": {
"isexe": "^2.0.0"
}
}
}
},
"listr": {
"version": "0.14.3",
"resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz",
@@ -7914,11 +8336,30 @@
"run-queue": "^1.0.3"
}
},
"mri": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz",
"integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==",
"dev": true
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"multimatch": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/multimatch/-/multimatch-4.0.0.tgz",
"integrity": "sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ==",
"dev": true,
"requires": {
"@types/minimatch": "^3.0.3",
"array-differ": "^3.0.0",
"array-union": "^2.1.0",
"arrify": "^2.0.1",
"minimatch": "^3.0.4"
}
},
"mustache": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/mustache/-/mustache-2.3.2.tgz",
@@ -8408,6 +8849,12 @@
"mimic-fn": "^2.1.0"
}
},
"opencollective-postinstall": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz",
"integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==",
"dev": true
},
"opener": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz",
@@ -8543,6 +8990,23 @@
"no-case": "^2.2.0"
}
},
"parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
"requires": {
"callsites": "^3.0.0"
},
"dependencies": {
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true
}
}
},
"parse-asn1": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz",
@@ -8701,6 +9165,15 @@
"find-up": "^3.0.0"
}
},
"please-upgrade-node": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz",
"integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==",
"dev": true,
"requires": {
"semver-compare": "^1.0.0"
}
},
"popper.js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
@@ -9709,6 +10182,144 @@
"utila": "~0.4"
}
},
"pretty-quick": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-2.0.1.tgz",
"integrity": "sha512-y7bJt77XadjUr+P1uKqZxFWLddvj3SKY6EU4BuQtMxmmEFSMpbN132pUWdSG1g1mtUfO0noBvn7wBf0BVeomHg==",
"dev": true,
"requires": {
"chalk": "^2.4.2",
"execa": "^2.1.0",
"find-up": "^4.1.0",
"ignore": "^5.1.4",
"mri": "^1.1.4",
"multimatch": "^4.0.0"
},
"dependencies": {
"cross-spawn": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz",
"integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==",
"dev": true,
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
}
},
"execa": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-2.1.0.tgz",
"integrity": "sha512-Y/URAVapfbYy2Xp/gb6A0E7iR8xeqOCXsuuaoMn7A5PzrXUK84E1gyiEfq0wQd/GHA6GsoHWwhNq8anb0mleIw==",
"dev": true,
"requires": {
"cross-spawn": "^7.0.0",
"get-stream": "^5.0.0",
"is-stream": "^2.0.0",
"merge-stream": "^2.0.0",
"npm-run-path": "^3.0.0",
"onetime": "^5.1.0",
"p-finally": "^2.0.0",
"signal-exit": "^3.0.2",
"strip-final-newline": "^2.0.0"
}
},
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dev": true,
"requires": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
}
},
"get-stream": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
"integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
"dev": true,
"requires": {
"pump": "^3.0.0"
}
},
"is-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
"integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
"dev": true
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dev": true,
"requires": {
"p-locate": "^4.1.0"
}
},
"npm-run-path": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz",
"integrity": "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==",
"dev": true,
"requires": {
"path-key": "^3.0.0"
}
},
"p-finally": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz",
"integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==",
"dev": true
},
"p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dev": true,
"requires": {
"p-limit": "^2.2.0"
}
},
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true
},
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true
},
"shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"requires": {
"shebang-regex": "^3.0.0"
}
},
"shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"requires": {
"isexe": "^2.0.0"
}
}
}
},
"pretty-time": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz",
@@ -10463,6 +11074,18 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
},
"semver-compare": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
"integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=",
"dev": true
},
"semver-regex": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz",
"integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==",
"dev": true
},
"send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
@@ -10646,6 +11269,12 @@
"xmlbuilder": "^13.0.0"
}
},
"slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true
},
"slice-ansi": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz",
@@ -11192,6 +11821,12 @@
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
},
"string-argv": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz",
"integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==",
"dev": true
},
"string-width": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
@@ -11260,6 +11895,17 @@
}
}
},
"stringify-object": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz",
"integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==",
"dev": true,
"requires": {
"get-own-enumerable-property-symbols": "^3.0.0",
"is-obj": "^1.0.1",
"is-regexp": "^1.0.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
@@ -13101,6 +13747,12 @@
"integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
"dev": true
},
"which-pm-runs": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
"integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=",
"dev": true
},
"wide-align": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
@@ -13280,6 +13932,15 @@
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
},
"yaml": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.7.2.tgz",
"integrity": "sha512-qXROVp90sb83XtAoqE8bP9RwAkTTZbugRUTm5YeFCBfNRPEp2YzTeqWiz7m5OORHzEvrA/qcGS8hp/E+MMROYw==",
"dev": true,
"requires": {
"@babel/runtime": "^7.6.3"
}
},
"yargs": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",

View File

@@ -1,47 +1,63 @@
{
"name": "postwoman",
"version": "1.9.0",
"description": "A free, fast and beautiful API request builder",
"author": "liyasthomas",
"private": true,
"scripts": {
"predev": "node build.js --dev",
"dev": "nuxt",
"prebuild": "node build.js",
"build": "nuxt build",
"start": "nuxt start",
"pregenerate": "node build.js",
"generate": "nuxt generate",
"e2e": "cypress run",
"e2e:open": "cypress open",
"dev:e2e": "server-test dev :3000 e2e:open",
"test": "start-server-and-test start http-get://localhost:3000 e2e"
},
"dependencies": {
"@nuxtjs/axios": "^5.9.5",
"@nuxtjs/google-analytics": "^2.2.3",
"@nuxtjs/google-tag-manager": "^2.3.1",
"@nuxtjs/pwa": "^3.0.0-beta.20",
"@nuxtjs/robots": "^2.4.2",
"@nuxtjs/sitemap": "^2.0.1",
"@nuxtjs/toast": "^3.3.0",
"ace-builds": "^1.4.8",
"firebase": "^7.9.1",
"graphql": "^14.6.0",
"graphql-language-service-interface": "^2.3.3",
"nuxt": "^2.11.0",
"nuxt-i18n": "^6.5.0",
"v-tooltip": "^2.0.3",
"vue-virtual-scroll-list": "^1.4.6",
"vuefire": "^2.2.1",
"vuejs-auto-complete": "^0.9.0",
"vuex-persist": "^2.2.0",
"yargs-parser": "^17.0.0"
},
"devDependencies": {
"cypress": "^4.0.2",
"node-sass": "^4.13.1",
"sass-loader": "^8.0.2",
"start-server-and-test": "^1.10.8"
"name": "postwoman",
"version": "1.9.0",
"description": "A free, fast and beautiful API request builder",
"author": "liyasthomas",
"private": true,
"scripts": {
"predev": "node build.js --dev",
"dev": "nuxt",
"prebuild": "node build.js",
"build": "nuxt build",
"start": "nuxt start",
"pregenerate": "node build.js",
"generate": "nuxt generate",
"e2e": "cypress run",
"e2e:open": "cypress open",
"dev:e2e": "server-test dev :3000 e2e:open",
"pretty-quick": "pretty-quick --pattern \"**/*.*(html|js|json|vue)\"",
"test": "start-server-and-test start http-get://localhost:3000 e2e"
},
"husky": {
"hooks": {
"pre-commit": "npm run pretty-quick"
}
},
"prettier": {
"trailingComma": "es5",
"semi": false,
"singleQuote": false,
"printWidth": 100
},
"dependencies": {
"@nuxtjs/axios": "^5.9.5",
"@nuxtjs/google-analytics": "^2.2.3",
"@nuxtjs/google-tag-manager": "^2.3.1",
"@nuxtjs/pwa": "^3.0.0-beta.20",
"@nuxtjs/robots": "^2.4.2",
"@nuxtjs/sitemap": "^2.0.1",
"@nuxtjs/toast": "^3.3.0",
"ace-builds": "^1.4.8",
"firebase": "^7.9.1",
"graphql": "^14.6.0",
"graphql-language-service-interface": "^2.3.3",
"nuxt": "^2.11.0",
"nuxt-i18n": "^6.5.0",
"v-tooltip": "^2.0.3",
"vue-virtual-scroll-list": "^1.4.6",
"vuefire": "^2.2.1",
"vuejs-auto-complete": "^0.9.0",
"vuex-persist": "^2.2.0",
"yargs-parser": "^17.0.0"
},
"devDependencies": {
"cypress": "^4.0.2",
"husky": "^4.2.3",
"lint-staged": "^10.0.8",
"node-sass": "^4.13.1",
"prettier": "^1.19.1",
"pretty-quick": "^2.0.1",
"sass-loader": "^8.0.2",
"start-server-and-test": "^1.10.8"
}
}

View File

@@ -11,11 +11,7 @@
<ul>
<li>
<label for="collectionUpload">
<button
class="icon"
@click="$refs.collectionUpload.click()"
v-tooltip="$t('json')"
>
<button class="icon" @click="$refs.collectionUpload.click()" v-tooltip="$t('json')">
<i class="material-icons">folder</i>
<span>{{ $t("import_collections") }}</span>
</button>
@@ -39,7 +35,7 @@
fontSize: '16px',
autoScrollEditorIntoView: true,
showPrintMargin: false,
useWorker: false
useWorker: false,
}"
/>
</li>
@@ -59,29 +55,17 @@
{{ $t("generate_docs_first") }}
</p>
<div>
<span
class="collection"
v-for="(collection, index) in this.items"
:key="index"
>
<span class="collection" v-for="(collection, index) in this.items" :key="index">
<h2>
<i class="material-icons">folder</i>
{{ collection.name || $t("none") }}
</h2>
<span
class="folder"
v-for="(folder, index) in collection.folders"
:key="index"
>
<span class="folder" v-for="(folder, index) in collection.folders" :key="index">
<h3>
<i class="material-icons">folder_open</i>
{{ folder.name || $t("none") }}
</h3>
<span
class="request"
v-for="(request, index) in folder.requests"
:key="index"
>
<span class="request" v-for="(request, index) in folder.requests" :key="index">
<h4>
<i class="material-icons">insert_drive_file</i>
{{ request.name || $t("none") }}
@@ -129,11 +113,7 @@
</p>
<h4 v-if="request.headers.length > 0">{{ $t("headers") }}</h4>
<span v-if="request.headers">
<p
v-for="header in request.headers"
:key="header.key"
class="doc-desc"
>
<p v-for="header in request.headers" :key="header.key" class="doc-desc">
<span>
{{ header.key || $t("none") }}:
<code>{{ header.value || $t("none") }}</code>
@@ -142,11 +122,7 @@
</span>
<h4 v-if="request.params.length > 0">{{ $t("parameters") }}</h4>
<span v-if="request.params">
<p
v-for="parameter in request.params"
:key="parameter.key"
class="doc-desc"
>
<p v-for="parameter in request.params" :key="parameter.key" class="doc-desc">
<span>
{{ parameter.key || $t("none") }}:
<code>{{ parameter.value || $t("none") }}</code>
@@ -155,11 +131,7 @@
</span>
<h4 v-if="request.bodyParam">{{ $t("payload") }}</h4>
<span v-if="request.bodyParam">
<p
v-for="payload in request.bodyParam"
:key="payload.key"
class="doc-desc"
>
<p v-for="payload in request.bodyParam" :key="payload.key" class="doc-desc">
<span>
{{ payload.key || $t("none") }}:
<code>{{ payload.value || $t("none") }}</code>
@@ -237,11 +209,7 @@
</p>
<h4 v-if="request.headers.length > 0">{{ $t("headers") }}</h4>
<span v-if="request.headers">
<p
v-for="header in request.headers"
:key="header.key"
class="doc-desc"
>
<p v-for="header in request.headers" :key="header.key" class="doc-desc">
<span>
{{ header.key || $t("none") }}:
<code>{{ header.value || $t("none") }}</code>
@@ -250,11 +218,7 @@
</span>
<h4 v-if="request.params.length > 0">{{ $t("parameters") }}</h4>
<span v-if="request.params">
<p
v-for="parameter in request.params"
:key="parameter.key"
class="doc-desc"
>
<p v-for="parameter in request.params" :key="parameter.key" class="doc-desc">
<span>
{{ parameter.key || $t("none") }}:
<code>{{ parameter.value || $t("none") }}</code>
@@ -263,11 +227,7 @@
</span>
<h4 v-if="request.bodyParam">{{ $t("payload") }}</h4>
<span v-if="request.bodyParam">
<p
v-for="payload in request.bodyParam"
:key="payload.key"
class="doc-desc"
>
<p v-for="payload in request.bodyParam" :key="payload.key" class="doc-desc">
<span>
{{ payload.key || $t("none") }}:
<code>{{ payload.value || $t("none") }}</code>
@@ -342,53 +302,53 @@
</style>
<script>
import AceEditor from "../components/ace-editor";
import AceEditor from "../components/ace-editor"
export default {
components: {
"pw-section": () => import("../components/section"),
Editor: AceEditor
Editor: AceEditor,
},
data() {
return {
collectionJSON: "[]",
items: []
};
items: [],
}
},
methods: {
uploadCollection() {
this.rawInput = true;
let file = this.$refs.collectionUpload.files[0];
this.rawInput = true
let file = this.$refs.collectionUpload.files[0]
if (file !== undefined && file !== null) {
let reader = new FileReader();
let reader = new FileReader()
reader.onload = ({ target }) => {
this.collectionJSON = target.result;
};
reader.readAsText(file);
this.collectionJSON = target.result
}
reader.readAsText(file)
this.$toast.info(this.$t("file_imported"), {
icon: "attach_file"
});
icon: "attach_file",
})
} else {
this.$toast.error(this.$t("choose_file"), {
icon: "attach_file"
});
icon: "attach_file",
})
}
},
getDoc() {
try {
this.items = JSON.parse(this.collectionJSON);
this.items = JSON.parse(this.collectionJSON)
this.$toast.info(this.$t("docs_generated"), {
icon: "book"
});
icon: "book",
})
} catch (e) {
this.$toast.error(e, {
icon: "code"
});
icon: "code",
})
}
}
}
};
},
},
}
</script>

View File

@@ -10,6 +10,7 @@
id="url"
type="url"
v-model="url"
spellcheck="false"
@keyup.enter="getSchema()"
/>
</li>
@@ -31,11 +32,7 @@
<div class="flex-wrap">
<label for="headerList">{{ $t("header_list") }}</label>
<div>
<button
class="icon"
@click="headers = []"
v-tooltip.bottom="$t('clear')"
>
<button class="icon" @click="headers = []" v-tooltip.bottom="$t('clear')">
<i class="material-icons">clear_all</i>
</button>
</div>
@@ -60,7 +57,7 @@
@input="
$store.commit('setGQLHeaderKey', {
index,
value: $event
value: $event,
})
"
autofocus
@@ -74,7 +71,7 @@
@change="
$store.commit('setGQLHeaderValue', {
index,
value: $event.target.value
value: $event.target.value,
})
"
autofocus
@@ -112,9 +109,7 @@
@click="ToggleExpandResponse"
ref="ToggleExpandResponse"
v-tooltip="{
content: !expandResponse
? $t('expand_response')
: $t('collapse_response')
content: !expandResponse ? $t('expand_response') : $t('collapse_response'),
}"
>
<i class="material-icons">
@@ -140,7 +135,7 @@
</div>
</div>
<Editor
:value="schemaString"
:value="schema"
:lang="'graphqlschema'"
:options="{
maxLines: responseBodyMaxLines,
@@ -149,20 +144,16 @@
autoScrollEditorIntoView: true,
readOnly: true,
showPrintMargin: false,
useWorker: false
useWorker: false,
}"
/>
</pw-section>
<pw-section class="cyan" :label="$t('query')" ref="query">
<div class="flex-wrap">
<div class="flex-wrap gqlRunQuery">
<label for="gqlQuery">{{ $t("query") }}</label>
<div>
<button
class="icon"
@click="runQuery()"
v-tooltip.bottom="$t('run_query')"
>
<button @click="runQuery()" v-tooltip.bottom="$t('run_query')">
<i class="material-icons">play_arrow</i>
</button>
<button
@@ -184,7 +175,7 @@
fontSize: '16px',
autoScrollEditorIntoView: true,
showPrintMargin: false,
useWorker: false
useWorker: false,
}"
/>
</pw-section>
@@ -199,7 +190,7 @@
fontSize: '16px',
autoScrollEditorIntoView: true,
showPrintMargin: false,
useWorker: false
useWorker: false,
}"
/>
</pw-section>
@@ -219,8 +210,9 @@
</div>
</div>
<Editor
:value="responseString"
:value="response"
:lang="'json'"
:lint="false"
:options="{
maxLines: responseBodyMaxLines,
minLines: '16',
@@ -228,7 +220,7 @@
autoScrollEditorIntoView: true,
readOnly: true,
showPrintMargin: false,
useWorker: false
useWorker: false,
}"
/>
</pw-section>
@@ -248,10 +240,7 @@
</label>
<div v-if="queryFields.length > 0" class="tab">
<div v-for="field in queryFields" :key="field.name">
<gql-field
:gqlField="field"
:jumpTypeCallback="handleJumpToType"
/>
<gql-field :gqlField="field" :jumpTypeCallback="handleJumpToType" />
</div>
</div>
@@ -267,10 +256,7 @@
</label>
<div v-if="mutationFields.length > 0" class="tab">
<div v-for="field in mutationFields" :key="field.name">
<gql-field
:gqlField="field"
:jumpTypeCallback="handleJumpToType"
/>
<gql-field :gqlField="field" :jumpTypeCallback="handleJumpToType" />
</div>
</div>
@@ -286,10 +272,7 @@
</label>
<div v-if="subscriptionFields.length > 0" class="tab">
<div v-for="field in subscriptionFields" :key="field.name">
<gql-field
:gqlField="field"
:jumpTypeCallback="handleJumpToType"
/>
<gql-field :gqlField="field" :jumpTypeCallback="handleJumpToType" />
</div>
</div>
@@ -304,15 +287,8 @@
{{ $t("types") }}
</label>
<div v-if="gqlTypes.length > 0" class="tab">
<div
v-for="type in gqlTypes"
:key="type.name"
:id="`type_${type.name}`"
>
<gql-type
:gqlType="type"
:jumpTypeCallback="handleJumpToType"
/>
<div v-for="type in gqlTypes" :key="type.name" :id="`type_${type.name}`">
<gql-type :gqlType="type" :jumpTypeCallback="handleJumpToType" />
</div>
</div>
</section>
@@ -339,20 +315,23 @@
max-height: calc(100vh - 186px);
overflow: auto;
}
.gqlRunQuery {
margin-bottom: 12px;
}
</style>
<script>
import axios from "axios";
import * as gql from "graphql";
import textareaAutoHeight from "../directives/textareaAutoHeight";
import { commonHeaders } from "../functions/headers";
import AceEditor from "../components/ace-editor";
import QueryEditor from "../components/graphql/queryeditor";
import { sendNetworkRequest } from "../functions/network";
import axios from "axios"
import * as gql from "graphql"
import textareaAutoHeight from "../directives/textareaAutoHeight"
import { commonHeaders } from "../functions/headers"
import AceEditor from "../components/ace-editor"
import QueryEditor from "../components/graphql/queryeditor"
import { sendNetworkRequest } from "../functions/network"
export default {
directives: {
textareaAutoHeight
textareaAutoHeight,
},
components: {
"pw-section": () => import("../components/section"),
@@ -360,212 +339,225 @@ export default {
"gql-type": () => import("../components/graphql/type"),
autocomplete: () => import("../components/autocomplete"),
Editor: AceEditor,
QueryEditor: QueryEditor
QueryEditor: QueryEditor,
},
data() {
return {
schemaString: "",
commonHeaders,
queryFields: [],
mutationFields: [],
subscriptionFields: [],
gqlTypes: [],
responseString: "",
copyButton: '<i class="material-icons">file_copy</i>',
downloadButton: '<i class="material-icons">get_app</i>',
doneButton: '<i class="material-icons">done</i>',
expandResponse: false,
responseBodyMaxLines: 16
};
responseBodyMaxLines: 16,
settings: {
SCROLL_INTO_ENABLED:
typeof this.$store.state.postwoman.settings.SCROLL_INTO_ENABLED !== "undefined"
? this.$store.state.postwoman.settings.SCROLL_INTO_ENABLED
: true,
},
}
},
computed: {
url: {
get() {
return this.$store.state.gql.url;
return this.$store.state.gql.url
},
set(value) {
this.$store.commit("setGQLState", { value, attribute: "url" });
}
this.$store.commit("setGQLState", { value, attribute: "url" })
},
},
headers: {
get() {
return this.$store.state.gql.headers;
return this.$store.state.gql.headers
},
set(value) {
this.$store.commit("setGQLState", { value, attribute: "headers" });
}
this.$store.commit("setGQLState", { value, attribute: "headers" })
},
},
gqlQueryString: {
get() {
return this.$store.state.gql.query;
return this.$store.state.gql.query
},
set(value) {
this.$store.commit("setGQLState", { value, attribute: "query" });
}
this.$store.commit("setGQLState", { value, attribute: "query" })
},
},
response: {
get() {
return this.$store.state.gql.response
},
set(value) {
this.$store.commit("setGQLState", { value, attribute: "response" })
},
},
schema: {
get() {
return this.$store.state.gql.schema
},
set(value) {
this.$store.commit("setGQLState", { value, attribute: "schema" })
},
},
variableString: {
get() {
return this.$store.state.gql.variablesJSONString;
return this.$store.state.gql.variablesJSONString
},
set(value) {
this.$store.commit("setGQLState", {
value,
attribute: "variablesJSONString"
});
}
attribute: "variablesJSONString",
})
},
},
headerString() {
const result = this.headers
.filter(({ key }) => !!key)
.map(({ key, value }) => `${key}: ${value}`)
.join(",\n");
return result === "" ? "" : `${result}`;
}
.join(",\n")
return result === "" ? "" : `${result}`
},
},
methods: {
handleJumpToType(type) {
const typesTab = document.getElementById("gqltypes-tab");
typesTab.checked = true;
const typesTab = document.getElementById("gqltypes-tab")
typesTab.checked = true
const rootTypeName = this.resolveRootType(type).name;
const rootTypeName = this.resolveRootType(type).name
const target = document.getElementById(`type_${rootTypeName}`);
if (target && this.$store.state.postwoman.settings.SCROLL_INTO_ENABLED) {
const target = document.getElementById(`type_${rootTypeName}`)
if (target && this.settings.SCROLL_INTO_ENABLED) {
target.scrollIntoView({
behavior: "smooth"
});
behavior: "smooth",
})
}
},
resolveRootType(type) {
let t = type;
while (t.ofType != null) t = t.ofType;
return t;
let t = type
while (t.ofType != null) t = t.ofType
return t
},
copySchema() {
this.$refs.copySchemaCode.innerHTML = this.doneButton;
const aux = document.createElement("textarea");
aux.innerText = this.schemaString;
document.body.appendChild(aux);
aux.select();
document.execCommand("copy");
document.body.removeChild(aux);
this.$refs.copySchemaCode.innerHTML = this.doneButton
const aux = document.createElement("textarea")
aux.innerText = this.schema
document.body.appendChild(aux)
aux.select()
document.execCommand("copy")
document.body.removeChild(aux)
this.$toast.success(this.$t("copied_to_clipboard"), {
icon: "done"
});
setTimeout(
() => (this.$refs.copySchemaCode.innerHTML = this.copyButton),
1000
);
icon: "done",
})
setTimeout(() => (this.$refs.copySchemaCode.innerHTML = this.copyButton), 1000)
},
copyQuery() {
this.$refs.copyQueryButton.innerHTML = this.doneButton;
const aux = document.createElement("textarea");
aux.innerText = this.gqlQueryString;
document.body.appendChild(aux);
aux.select();
document.execCommand("copy");
document.body.removeChild(aux);
this.$refs.copyQueryButton.innerHTML = this.doneButton
const aux = document.createElement("textarea")
aux.innerText = this.gqlQueryString
document.body.appendChild(aux)
aux.select()
document.execCommand("copy")
document.body.removeChild(aux)
this.$toast.success(this.$t("copied_to_clipboard"), {
icon: "done"
});
setTimeout(
() => (this.$refs.copyQueryButton.innerHTML = this.copyButton),
1000
);
icon: "done",
})
setTimeout(() => (this.$refs.copyQueryButton.innerHTML = this.copyButton), 1000)
},
copyResponse() {
this.$refs.copyResponseButton.innerHTML = this.doneButton;
const aux = document.createElement("textarea");
aux.innerText = this.responseString;
document.body.appendChild(aux);
aux.select();
document.execCommand("copy");
document.body.removeChild(aux);
this.$refs.copyResponseButton.innerHTML = this.doneButton
const aux = document.createElement("textarea")
aux.innerText = this.response
document.body.appendChild(aux)
aux.select()
document.execCommand("copy")
document.body.removeChild(aux)
this.$toast.success(this.$t("copied_to_clipboard"), {
icon: "done"
});
setTimeout(
() => (this.$refs.copyResponseButton.innerHTML = this.copyButton),
1000
);
icon: "done",
})
setTimeout(() => (this.$refs.copyResponseButton.innerHTML = this.copyButton), 1000)
},
async runQuery() {
const startTime = Date.now();
this.$nuxt.$loading.start();
this.$store.state.postwoman.settings.SCROLL_INTO_ENABLED &&
this.scrollInto("response");
try {
let headers = {};
this.headers.forEach(header => {
headers[header.key] = header.value;
});
let variables = JSON.parse(this.variableString);
const gqlQueryString = this.gqlQueryString;
const reqOptions = {
method: "post",
url: this.url,
headers: {
...headers,
"content-type": "application/json"
},
data: JSON.stringify({ query: gqlQueryString, variables })
};
const data = await sendNetworkRequest(reqOptions, this.$store);
this.responseString = JSON.stringify(data.data, null, 2);
this.$nuxt.$loading.finish();
const duration = Date.now() - startTime;
this.$toast.info(this.$t("finished_in", { duration }), {
icon: "done"
});
} catch (error) {
this.$nuxt.$loading.finish();
this.$toast.error(`${error} ${this.$t("f12_details")}`, {
icon: "error"
});
console.log("Error", error);
}
},
async getSchema() {
const startTime = Date.now();
this.schemaString = this.$t("loading");
this.$store.state.postwoman.settings.SCROLL_INTO_ENABLED &&
this.scrollInto("schema");
const startTime = Date.now()
// Start showing the loading bar as soon as possible.
// The nuxt axios module will hide it when the request is made.
this.$nuxt.$loading.start();
this.$nuxt.$loading.start()
this.response = this.$t("loading")
if (this.settings.SCROLL_INTO_ENABLED) this.scrollInto("response")
try {
const query = JSON.stringify({
query: gql.getIntrospectionQuery()
});
let headers = {};
let headers = {}
this.headers.forEach(header => {
headers[header.key] = header.value;
});
headers[header.key] = header.value
})
let variables = JSON.parse(this.variableString)
const gqlQueryString = this.gqlQueryString
const reqOptions = {
method: "post",
url: this.url,
headers: {
...headers,
"content-type": "application/json"
"content-type": "application/json",
},
data: query
};
data: JSON.stringify({ query: gqlQueryString, variables }),
}
// console.log(reqOptions);
const data = await sendNetworkRequest(reqOptions, this.$store)
this.response = JSON.stringify(data.data, null, 2)
this.$nuxt.$loading.finish()
const duration = Date.now() - startTime
this.$toast.info(this.$t("finished_in", { duration }), {
icon: "done",
})
} catch (error) {
this.response = `${error}. ${this.$t("check_console_details")}`
this.$nuxt.$loading.finish()
this.$toast.error(`${error} ${this.$t("f12_details")}`, {
icon: "error",
})
console.log("Error", error)
}
},
async getSchema() {
const startTime = Date.now()
// Start showing the loading bar as soon as possible.
// The nuxt axios module will hide it when the request is made.
this.$nuxt.$loading.start()
this.schema = this.$t("loading")
if (this.settings.SCROLL_INTO_ENABLED) this.scrollInto("schema")
try {
const query = JSON.stringify({
query: gql.getIntrospectionQuery(),
})
let headers = {}
this.headers.forEach(header => {
headers[header.key] = header.value
})
const reqOptions = {
method: "post",
url: this.url,
headers: {
...headers,
"content-type": "application/json",
},
data: query,
}
const reqConfig = this.$store.state.postwoman.settings.PROXY_ENABLED
? {
@@ -575,140 +567,130 @@ export default {
`https://postwoman.apollosoftware.xyz/`,
data: reqOptions
}
: reqOptions;
: reqOptions
const res = await axios(reqConfig);
const res = await axios(reqConfig)
const data = this.$store.state.postwoman.settings.PROXY_ENABLED
? res.data
: res;
const schema = gql.buildClientSchema(data.data.data);
this.schemaString = gql.printSchema(schema, {
commentDescriptions: true
});
const data = this.$store.state.postwoman.settings.PROXY_ENABLED ? res.data : res
const schema = gql.buildClientSchema(data.data.data)
this.schema = gql.printSchema(schema, {
commentDescriptions: true,
})
if (schema.getQueryType()) {
const fields = schema.getQueryType().getFields();
const qFields = [];
const fields = schema.getQueryType().getFields()
const qFields = []
for (const field in fields) {
qFields.push(fields[field]);
qFields.push(fields[field])
}
this.queryFields = qFields;
this.queryFields = qFields
}
if (schema.getMutationType()) {
const fields = schema.getMutationType().getFields();
const mFields = [];
const fields = schema.getMutationType().getFields()
const mFields = []
for (const field in fields) {
mFields.push(fields[field]);
mFields.push(fields[field])
}
this.mutationFields = mFields;
this.mutationFields = mFields
}
if (schema.getSubscriptionType()) {
const fields = schema.getSubscriptionType().getFields();
const sFields = [];
const fields = schema.getSubscriptionType().getFields()
const sFields = []
for (const field in fields) {
sFields.push(fields[field]);
sFields.push(fields[field])
}
this.subscriptionFields = sFields;
this.subscriptionFields = sFields
}
const typeMap = schema.getTypeMap();
const types = [];
const typeMap = schema.getTypeMap()
const types = []
const queryTypeName = schema.getQueryType()
? schema.getQueryType().name
: "";
const mutationTypeName = schema.getMutationType()
? schema.getMutationType().name
: "";
const queryTypeName = schema.getQueryType() ? schema.getQueryType().name : ""
const mutationTypeName = schema.getMutationType() ? schema.getMutationType().name : ""
const subscriptionTypeName = schema.getSubscriptionType()
? schema.getSubscriptionType().name
: "";
: ""
for (const type in typeMap) {
if (
!typeMap[type].name.startsWith("__") &&
![queryTypeName, mutationTypeName, subscriptionTypeName].includes(
typeMap[type].name
) &&
![queryTypeName, mutationTypeName, subscriptionTypeName].includes(typeMap[type].name) &&
typeMap[type] instanceof gql.GraphQLObjectType
) {
types.push(typeMap[type]);
types.push(typeMap[type])
}
}
this.gqlTypes = types;
this.$refs.queryEditor.setValidationSchema(schema);
this.$nuxt.$loading.finish();
const duration = Date.now() - startTime;
this.gqlTypes = types
this.$refs.queryEditor.setValidationSchema(schema)
this.$nuxt.$loading.finish()
const duration = Date.now() - startTime
this.$toast.info(this.$t("finished_in", { duration }), {
icon: "done"
});
icon: "done",
})
} catch (error) {
this.$nuxt.$loading.finish();
this.schemaString = `${error}. ${this.$t("check_console_details")}`;
this.$nuxt.$loading.finish()
this.schema = `${error}. ${this.$t("check_console_details")}`
this.$toast.error(`${error} ${this.$t("f12_details")}`, {
icon: "error"
});
console.log("Error", error);
icon: "error",
})
console.log("Error", error)
}
},
ToggleExpandResponse() {
this.expandResponse = !this.expandResponse;
this.responseBodyMaxLines =
this.responseBodyMaxLines == Infinity ? 16 : Infinity;
this.expandResponse = !this.expandResponse
this.responseBodyMaxLines = this.responseBodyMaxLines == Infinity ? 16 : Infinity
},
downloadResponse() {
const dataToWrite = JSON.stringify(this.schemaString, null, 2);
const file = new Blob([dataToWrite], { type: "application/json" });
const a = document.createElement("a");
const url = URL.createObjectURL(file);
a.href = url;
a.download = `${this.url} on ${Date()}.graphql`.replace(/\./g, "[dot]");
document.body.appendChild(a);
a.click();
this.$refs.downloadResponse.innerHTML = this.doneButton;
const dataToWrite = JSON.stringify(this.schema, null, 2)
const file = new Blob([dataToWrite], { type: "application/json" })
const a = document.createElement("a")
const url = URL.createObjectURL(file)
a.href = url
a.download = `${this.url} on ${Date()}.graphql`.replace(/\./g, "[dot]")
document.body.appendChild(a)
a.click()
this.$refs.downloadResponse.innerHTML = this.doneButton
this.$toast.success(this.$t("download_started"), {
icon: "done"
});
icon: "done",
})
setTimeout(() => {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
this.$refs.downloadResponse.innerHTML = this.downloadButton;
}, 1000);
document.body.removeChild(a)
window.URL.revokeObjectURL(url)
this.$refs.downloadResponse.innerHTML = this.downloadButton
}, 1000)
},
addRequestHeader(index) {
this.$store.commit("addGQLHeader", {
key: "",
value: ""
});
return false;
value: "",
})
return false
},
removeRequestHeader(index) {
// .slice() is used so we get a separate array, rather than just a reference
const oldHeaders = this.headers.slice();
const oldHeaders = this.headers.slice()
this.$store.commit("removeGQLHeader", index);
this.$store.commit("removeGQLHeader", index)
this.$toast.error(this.$t("deleted"), {
icon: "delete",
action: {
text: this.$t("undo"),
duration: 4000,
onClick: (e, toastObject) => {
this.headers = oldHeaders;
toastObject.remove();
}
}
});
// console.log(oldHeaders);
this.headers = oldHeaders
toastObject.remove()
},
},
})
},
scrollInto(view) {
this.$refs[view].$el.scrollIntoView({
behavior: "smooth"
});
}
}
};
behavior: "smooth",
})
},
},
}
</script>

File diff suppressed because it is too large Load Diff

2937
pages/index.vue.orig Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,7 @@
<input
id="url"
type="url"
spellcheck="false"
:class="{ error: !urlValid }"
v-model="url"
@keyup.enter="urlValid ? toggleConnection() : null"
@@ -19,12 +20,7 @@
<div>
<li>
<label for="connect" class="hide-on-small-screen">&nbsp;</label>
<button
:disabled="!urlValid"
id="connect"
name="connect"
@click="toggleConnection"
>
<button :disabled="!urlValid" id="connect" name="connect" @click="toggleConnection">
{{ !connectionState ? $t("connect") : $t("disconnect") }}
<span>
<i class="material-icons">
@@ -37,12 +33,7 @@
</ul>
</pw-section>
<pw-section
class="purple"
:label="$t('communication')"
id="response"
ref="response"
>
<pw-section class="purple" :label="$t('communication')" id="response" ref="response">
<ul>
<li>
<label for="log">{{ $t("log") }}</label>
@@ -75,12 +66,7 @@
<div>
<li>
<label for="send" class="hide-on-small-screen">&nbsp;</label>
<button
id="send"
name="send"
:disabled="!connectionState"
@click="sendMessage"
>
<button id="send" name="send" :disabled="!connectionState" @click="sendMessage">
{{ $t("send") }}
<span>
<i class="material-icons">send</i>
@@ -127,12 +113,7 @@
</ul>
</pw-section>
<pw-section
class="purple"
:label="$t('communication')"
id="response"
ref="response"
>
<pw-section class="purple" :label="$t('communication')" id="response" ref="response">
<ul>
<li>
<label for="log">{{ $t("events") }}</label>
@@ -187,7 +168,7 @@ div.log {
<script>
export default {
components: {
"pw-section": () => import("../components/section")
"pw-section": () => import("../components/section"),
},
data() {
return {
@@ -196,132 +177,132 @@ export default {
socket: null,
communication: {
log: null,
input: ""
input: "",
},
connectionSSEState: false,
server: "https://express-eventsource.herokuapp.com/events",
sse: null,
events: {
log: null,
input: ""
}
};
input: "",
},
}
},
computed: {
urlValid() {
const protocol = "^(wss?:\\/\\/)?";
const protocol = "^(wss?:\\/\\/)?"
const validIP = new RegExp(
`${protocol}(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$`
);
)
const validHostname = new RegExp(
`${protocol}(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]).)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9/])$`
);
return validIP.test(this.url) || validHostname.test(this.url);
)
return validIP.test(this.url) || validHostname.test(this.url)
},
serverValid() {
const protocol = "^(https?:\\/\\/)?";
const protocol = "^(https?:\\/\\/)?"
const validIP = new RegExp(
`${protocol}(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$`
);
)
const validHostname = new RegExp(
`${protocol}(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]).)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9/])$`
);
return validIP.test(this.server) || validHostname.test(this.server);
}
)
return validIP.test(this.server) || validHostname.test(this.server)
},
},
methods: {
toggleConnection() {
// If it is connecting:
if (!this.connectionState) return this.connect();
if (!this.connectionState) return this.connect()
// Otherwise, it's disconnecting.
else return this.disconnect();
else return this.disconnect()
},
connect() {
this.communication.log = [
{
payload: this.$t("connecting_to", { name: this.url }),
source: "info",
color: "var(--ac-color)"
}
];
color: "var(--ac-color)",
},
]
try {
this.socket = new WebSocket(this.url);
this.socket = new WebSocket(this.url)
this.socket.onopen = event => {
this.connectionState = true;
this.connectionState = true
this.communication.log = [
{
payload: this.$t("connected_to", { name: this.url }),
source: "info",
color: "var(--ac-color)",
ts: new Date().toLocaleTimeString()
}
];
ts: new Date().toLocaleTimeString(),
},
]
this.$toast.success(this.$t("connected"), {
icon: "sync"
});
};
icon: "sync",
})
}
this.socket.onerror = event => {
this.handleError();
};
this.handleError()
}
this.socket.onclose = event => {
this.connectionState = false;
this.connectionState = false
this.communication.log.push({
payload: this.$t("disconnected_from", { name: this.url }),
source: "info",
color: "#ff5555",
ts: new Date().toLocaleTimeString()
});
ts: new Date().toLocaleTimeString(),
})
this.$toast.error(this.$t("disconnected"), {
icon: "sync_disabled"
});
};
icon: "sync_disabled",
})
}
this.socket.onmessage = event => {
this.communication.log.push({
payload: event.data,
source: "server",
ts: new Date().toLocaleTimeString()
});
};
ts: new Date().toLocaleTimeString(),
})
}
} catch (ex) {
this.handleError(ex);
this.handleError(ex)
this.$toast.error(this.$t("something_went_wrong"), {
icon: "error"
});
icon: "error",
})
}
},
disconnect() {
this.socket.close();
this.socket.close()
},
handleError(error) {
this.disconnect();
this.connectionState = false;
this.disconnect()
this.connectionState = false
this.communication.log.push({
payload: this.$t("error_occurred"),
source: "info",
color: "#ff5555",
ts: new Date().toLocaleTimeString()
});
ts: new Date().toLocaleTimeString(),
})
if (error !== null)
this.communication.log.push({
payload: error,
source: "info",
color: "#ff5555",
ts: new Date().toLocaleTimeString()
});
ts: new Date().toLocaleTimeString(),
})
},
sendMessage() {
const message = this.communication.input;
this.socket.send(message);
const message = this.communication.input
this.socket.send(message)
this.communication.log.push({
payload: message,
source: "client",
ts: new Date().toLocaleTimeString()
});
this.communication.input = "";
ts: new Date().toLocaleTimeString(),
})
this.communication.input = ""
},
collapse({ target }) {
const el = target.parentNode.className;
document.getElementsByClassName(el)[0].classList.toggle("hidden");
const el = target.parentNode.className
document.getElementsByClassName(el)[0].classList.toggle("hidden")
},
getSourcePrefix(source) {
const sourceEmojis = {
@@ -330,70 +311,69 @@ export default {
// Source used for client to server messages.
client: "\t👽 [SENT]:\t",
// Source used for server to client messages.
server: "\t📥 [RECEIVED]:\t"
};
if (Object.keys(sourceEmojis).includes(source))
return sourceEmojis[source];
return "";
server: "\t📥 [RECEIVED]:\t",
}
if (Object.keys(sourceEmojis).includes(source)) return sourceEmojis[source]
return ""
},
toggleSSEConnection() {
// If it is connecting:
if (!this.connectionSSEState) return this.start();
if (!this.connectionSSEState) return this.start()
// Otherwise, it's disconnecting.
else return this.stop();
else return this.stop()
},
start() {
this.events.log = [
{
payload: this.$t("connecting_to", { name: this.server }),
source: "info",
color: "var(--ac-color)"
}
];
color: "var(--ac-color)",
},
]
if (typeof EventSource !== "undefined") {
try {
this.sse = new EventSource(this.server);
this.sse = new EventSource(this.server)
this.sse.onopen = event => {
this.connectionSSEState = true;
this.connectionSSEState = true
this.events.log = [
{
payload: this.$t("connected_to", { name: this.server }),
source: "info",
color: "var(--ac-color)",
ts: new Date().toLocaleTimeString()
}
];
ts: new Date().toLocaleTimeString(),
},
]
this.$toast.success(this.$t("connected"), {
icon: "sync"
});
};
icon: "sync",
})
}
this.sse.onerror = event => {
this.handleSSEError();
};
this.handleSSEError()
}
this.sse.onclose = event => {
this.connectionSSEState = false;
this.connectionSSEState = false
this.events.log.push({
payload: this.$t("disconnected_from", { name: this.server }),
source: "info",
color: "#ff5555",
ts: new Date().toLocaleTimeString()
});
ts: new Date().toLocaleTimeString(),
})
this.$toast.error(this.$t("disconnected"), {
icon: "sync_disabled"
});
};
icon: "sync_disabled",
})
}
this.sse.onmessage = event => {
this.events.log.push({
payload: event.data,
source: "server",
ts: new Date().toLocaleTimeString()
});
};
ts: new Date().toLocaleTimeString(),
})
}
} catch (ex) {
this.handleSSEError(ex);
this.handleSSEError(ex)
this.$toast.error(this.$t("something_went_wrong"), {
icon: "error"
});
icon: "error",
})
}
} else {
this.events.log = [
@@ -401,38 +381,38 @@ export default {
payload: this.$t("browser_support_sse"),
source: "info",
color: "#ff5555",
ts: new Date().toLocaleTimeString()
}
];
ts: new Date().toLocaleTimeString(),
},
]
}
},
handleSSEError(error) {
this.stop();
this.connectionSSEState = false;
this.stop()
this.connectionSSEState = false
this.events.log.push({
payload: this.$t("error_occurred"),
source: "info",
color: "#ff5555",
ts: new Date().toLocaleTimeString()
});
ts: new Date().toLocaleTimeString(),
})
if (error !== null)
this.events.log.push({
payload: error,
source: "info",
color: "#ff5555",
ts: new Date().toLocaleTimeString()
});
ts: new Date().toLocaleTimeString(),
})
},
stop() {
this.sse.onclose();
this.sse.close();
}
this.sse.onclose()
this.sse.close()
},
},
updated: function() {
this.$nextTick(function() {
const divLog = document.getElementById("log");
divLog.scrollBy(0, divLog.scrollHeight + 100);
});
}
};
const divLog = document.getElementById("log")
divLog.scrollBy(0, divLog.scrollHeight + 100)
})
},
}
</script>

View File

@@ -12,14 +12,14 @@
/>
<i v-else class="material-icons">account_circle</i>
<span>
{{ fb.currentUser.displayName || "Name not found" }}
{{ fb.currentUser.displayName || $t("nothing_found") }}
</span>
</button>
<br />
<button class="icon">
<i class="material-icons">email</i>
<span>
{{ fb.currentUser.email || "Email not found" }}
{{ fb.currentUser.email || $t("nothing_found") }}
</span>
</button>
<br />
@@ -88,18 +88,14 @@
<li>
<label>{{ $t("background") }}</label>
<div class="backgrounds">
<span
:key="theme.class"
@click="applyTheme(theme)"
v-for="theme in themes"
>
<span :key="theme.class" @click="applyTheme(theme)" v-for="theme in themes">
<swatch
:active="settings.THEME_CLASS === theme.class"
:class="{ vibrant: theme.vibrant }"
:color="theme.color"
:name="theme.name"
class="bg"
></swatch>
/>
</span>
</div>
</li>
@@ -110,7 +106,7 @@
<div class="colors">
<span
:key="entry.color"
@click.prevent="setActiveColor(entry.color, entry.vibrant)"
@click="setActiveColor(entry.color, entry.vibrant)"
v-for="entry in colors"
>
<swatch
@@ -132,14 +128,12 @@
@change="toggleSetting('FRAME_COLORS_ENABLED')"
>
{{ $t("multi_color") }}
{{
settings.FRAME_COLORS_ENABLED ? $t("enabled") : $t("disabled")
}}
{{ settings.FRAME_COLORS_ENABLED ? $t("enabled") : $t("disabled") }}
</pw-toggle>
</span>
</li>
</ul>
<ul>
</ul>
<ul>
<li>
<span>
<pw-toggle
@@ -147,9 +141,7 @@
@change="toggleSetting('SCROLL_INTO_ENABLED')"
>
{{ $t("scrollInto_use_toggle") }}
{{
settings.SCROLL_INTO_ENABLED ? $t("enabled") : $t("disabled")
}}
{{ settings.SCROLL_INTO_ENABLED ? $t("enabled") : $t("disabled") }}
</pw-toggle>
</span>
</li>
@@ -176,10 +168,7 @@
<li>
<div class="flex-wrap">
<span>
<pw-toggle
:on="settings.PROXY_ENABLED"
@change="toggleSetting('PROXY_ENABLED')"
>
<pw-toggle :on="settings.PROXY_ENABLED" @change="toggleSetting('PROXY_ENABLED')">
{{ $t("proxy") }}
{{ settings.PROXY_ENABLED ? $t("enabled") : $t("disabled") }}
</pw-toggle>
@@ -200,11 +189,7 @@
<li>
<div class="flex-wrap">
<label for="url">{{ $t("url") }}</label>
<button
class="icon"
@click="resetProxy"
v-tooltip.bottom="$t('reset_default')"
>
<button class="icon" @click="resetProxy" v-tooltip.bottom="$t('reset_default')">
<i class="material-icons">clear_all</i>
</button>
</div>
@@ -226,10 +211,8 @@
class="link"
href="https://apollosoftware.xyz/legal/postwoman"
target="_blank"
rel="noopener"
>
{{ $t("apollosw_privacy_policy") }} </a
>.
rel="noopener">
{{ $t("apollosw_privacy_policy") }} </a>.
</p>
</li>
</ul>
@@ -255,14 +238,14 @@
<style scoped lang="scss"></style>
<script>
import firebase from "firebase/app";
import { fb } from "../functions/fb";
import firebase from "firebase/app"
import { fb } from "../functions/fb"
export default {
components: {
"pw-section": () => import("../components/section"),
"pw-toggle": () => import("../components/toggle"),
swatch: () => import("../components/settings/swatch")
swatch: () => import("../components/settings/swatch"),
},
data() {
@@ -275,20 +258,20 @@ export default {
color: "#202124",
name: this.$t("kinda_dark"),
class: "",
aceEditor: "twilight"
aceEditor: "twilight",
},
{
color: "#ffffff",
name: this.$t("clearly_white"),
vibrant: true,
class: "light",
aceEditor: "iplastic"
aceEditor: "iplastic",
},
{
color: "#000000",
name: this.$t("just_black"),
class: "black",
aceEditor: "vibrant_ink"
aceEditor: "vibrant_ink",
},
{
color: "var(--ac-color)",
@@ -297,8 +280,8 @@ export default {
class: "auto",
aceEditor: window.matchMedia("(prefers-color-scheme: light)").matches
? "iplastic"
: "twilight"
}
: "twilight",
},
],
// You can define a new color here! It will simply store the color value.
colors: [
@@ -306,43 +289,43 @@ export default {
{
color: "#50fa7b",
name: this.$t("green"),
vibrant: true
vibrant: true,
},
{
color: "#f1fa8c",
name: this.$t("yellow"),
vibrant: true
vibrant: true,
},
{
color: "#ff79c6",
name: this.$t("pink"),
vibrant: true
vibrant: true,
},
{
color: "#ff5555",
name: this.$t("red"),
vibrant: false
vibrant: false,
},
{
color: "#bd93f9",
name: this.$t("purple"),
vibrant: true
vibrant: true,
},
{
color: "#ffb86c",
name: this.$t("orange"),
vibrant: true
vibrant: true,
},
{
color: "#8be9fd",
name: this.$t("cyan"),
vibrant: true
vibrant: true,
},
{
color: "#57b5f9",
name: this.$t("blue"),
vibrant: false
}
vibrant: false,
},
],
settings: {
@@ -352,95 +335,87 @@ export default {
? this.$store.state.postwoman.settings.SCROLL_INTO_ENABLED
: true,
THEME_CLASS: "",
THEME_COLOR: "",
THEME_TAB_COLOR: "",
THEME_COLOR_VIBRANT: true,
FRAME_COLORS_ENABLED:
this.$store.state.postwoman.settings.FRAME_COLORS_ENABLED || false,
PROXY_ENABLED:
this.$store.state.postwoman.settings.PROXY_ENABLED || false,
PROXY_URL:
this.$store.state.postwoman.settings.PROXY_URL ||
"https://postwoman.apollosoftware.xyz/",
FRAME_COLORS_ENABLED: this.$store.state.postwoman.settings.FRAME_COLORS_ENABLED || false,
PROXY_ENABLED: this.$store.state.postwoman.settings.PROXY_ENABLED || false,
PROXY_URL: this.$store.state.postwoman.settings.PROXY_URL || "https://postwoman.apollosoftware.xyz/",
PROXY_KEY: this.$store.state.postwoman.settings.PROXY_KEY || "",
EXTENSIONS_ENABLED:
typeof this.$store.state.postwoman.settings.EXTENSIONS_ENABLED !==
"undefined"
typeof this.$store.state.postwoman.settings.EXTENSIONS_ENABLED !== "undefined"
? this.$store.state.postwoman.settings.EXTENSIONS_ENABLED
: true
: true,
},
doneButton: '<i class="material-icons">done</i>',
fb
};
fb,
}
},
watch: {
proxySettings: {
deep: true,
handler(value) {
this.applySetting("PROXY_URL", value.url);
this.applySetting("PROXY_KEY", value.key);
}
}
this.applySetting("PROXY_URL", value.url)
this.applySetting("PROXY_KEY", value.key)
},
},
},
methods: {
applyTheme({ class: name, color, aceEditor }) {
this.applySetting("THEME_CLASS", name);
this.applySetting("THEME_ACE_EDITOR", aceEditor);
document
.querySelector("meta[name=theme-color]")
.setAttribute("content", color);
this.applySetting("THEME_TAB_COLOR", color);
document.documentElement.className = name;
this.applySetting("THEME_CLASS", name)
this.applySetting("THEME_ACE_EDITOR", aceEditor)
document.querySelector("meta[name=theme-color]").setAttribute("content", color)
this.applySetting("THEME_TAB_COLOR", color)
document.documentElement.className = name
},
setActiveColor(color, vibrant) {
// By default, the color is vibrant.
if (vibrant === null) vibrant = true;
document.documentElement.style.setProperty("--ac-color", color);
if (vibrant === null) vibrant = true
document.documentElement.style.setProperty("--ac-color", color)
document.documentElement.style.setProperty(
"--act-color",
vibrant ? "rgba(32, 33, 36, 1)" : "rgba(255, 255, 255, 1)"
);
this.applySetting("THEME_COLOR", color.toUpperCase());
this.applySetting("THEME_COLOR_VIBRANT", vibrant);
)
this.applySetting("THEME_COLOR", color.toUpperCase())
this.applySetting("THEME_COLOR_VIBRANT", vibrant)
},
getActiveColor() {
// This strips extra spaces and # signs from the strings.
const strip = str => str.replace(/#/g, "").replace(/ /g, "");
const strip = str => str.replace(/#/g, "").replace(/ /g, "")
return `#${strip(
window
.getComputedStyle(document.documentElement)
.getPropertyValue("--ac-color")
).toUpperCase()}`;
window.getComputedStyle(document.documentElement).getPropertyValue("--ac-color")
).toUpperCase()}`
},
applySetting(key, value) {
this.settings[key] = value;
this.$store.commit("postwoman/applySetting", [key, value]);
this.settings[key] = value
this.$store.commit("postwoman/applySetting", [key, value])
},
toggleSetting(key) {
this.settings[key] = !this.settings[key];
this.$store.commit("postwoman/applySetting", [key, this.settings[key]]);
this.settings[key] = !this.settings[key]
this.$store.commit("postwoman/applySetting", [key, this.settings[key]])
},
logout() {
fb.currentUser = null;
fb.currentUser = null
firebase
.auth()
.signOut()
.catch(err => {
this.$toast.show(err.message || err, {
icon: "error"
});
});
icon: "error",
})
})
this.$toast.info(this.$t("logged_out"), {
icon: "vpn_key"
});
icon: "vpn_key",
})
},
signInWithGoogle() {
const provider = new firebase.auth.GoogleAuthProvider();
const provider = new firebase.auth.GoogleAuthProvider()
firebase
.auth()
.signInWithPopup(provider)
@@ -453,24 +428,24 @@ export default {
action: {
text: this.$t("yes"),
onClick: (e, toastObject) => {
fb.writeSettings("syncHistory", true);
fb.writeSettings("syncCollections", true);
fb.writeSettings("syncEnvironments", true);
this.$router.push({ path: "/settings" });
toastObject.remove();
}
}
});
fb.writeSettings("syncHistory", true)
fb.writeSettings("syncCollections", true)
fb.writeSettings("syncEnvironments", true)
this.$router.push({ path: "/settings" })
toastObject.remove()
},
},
})
}
})
.catch(err => {
this.$toast.show(err.message || err, {
icon: "error"
});
});
icon: "error",
})
})
},
signInWithGithub() {
const provider = new firebase.auth.GithubAuthProvider();
const provider = new firebase.auth.GithubAuthProvider()
firebase
.auth()
.signInWithPopup(provider)
@@ -483,54 +458,51 @@ export default {
action: {
text: this.$t("yes"),
onClick: (e, toastObject) => {
fb.writeSettings("syncHistory", true);
fb.writeSettings("syncCollections", true);
fb.writeSettings("syncEnvironments", true);
this.$router.push({ path: "/settings" });
toastObject.remove();
}
}
});
fb.writeSettings("syncHistory", true)
fb.writeSettings("syncCollections", true)
fb.writeSettings("syncEnvironments", true)
this.$router.push({ path: "/settings" })
toastObject.remove()
},
},
})
}
})
.catch(err => {
this.$toast.show(err.message || err, {
icon: "error"
});
});
icon: "error",
})
})
},
toggleSettings(s, v) {
fb.writeSettings(s, !v);
fb.writeSettings(s, !v)
},
initSettings() {
fb.writeSettings("syncHistory", true);
fb.writeSettings("syncCollections", true);
fb.writeSettings("syncEnvironments", true);
fb.writeSettings("syncHistory", true)
fb.writeSettings("syncCollections", true)
fb.writeSettings("syncEnvironments", true)
},
resetProxy({ target }) {
this.settings.PROXY_URL = `https://postwoman.apollosoftware.xyz/`;
target.innerHTML = this.doneButton;
this.$toast.info(this.$t("cleared"), {
icon: "clear_all"
});
setTimeout(
() => (target.innerHTML = '<i class="material-icons">clear_all</i>'),
1000
);
}
icon: "clear_all",
})
setTimeout(() => (target.innerHTML = '<i class="material-icons">clear_all</i>'), 1000)
},
},
beforeMount() {
this.settings.THEME_COLOR = this.getActiveColor();
this.settings.THEME_COLOR = this.getActiveColor()
},
computed: {
proxySettings() {
return {
url: this.settings.PROXY_URL,
key: this.settings.PROXY_KEY
};
}
}
};
key: this.settings.PROXY_KEY,
}
},
},
}
</script>

View File

@@ -1,4 +1,4 @@
import Vue from "vue";
import VTooltip from "v-tooltip";
import Vue from "vue"
import VTooltip from "v-tooltip"
Vue.use(VTooltip);
Vue.use(VTooltip)

View File

@@ -1,5 +1,5 @@
import VuexPersistence from "vuex-persist";
import VuexPersistence from "vuex-persist"
export default ({ store }) => {
new VuexPersistence().plugin(store);
};
new VuexPersistence().plugin(store)
}

View File

@@ -1,24 +1,24 @@
import Vuex from "vuex";
import state from "./state";
import VuexPersist from "vuex-persist";
import Vuex from "vuex"
import state from "./state"
import VuexPersist from "vuex-persist"
export default {
install(Vue) {
Vue.use(Vuex);
Vue.use(Vuex)
const vuexLocalStorage = new VuexPersist({
key: "vuex",
storage: window.localStorage,
reducer: ({ ...request }) => ({
...request
})
});
...request,
}),
})
const store = new Vuex.Store({
state,
plugins: [vuexLocalStorage.plugin]
});
plugins: [vuexLocalStorage.plugin],
})
Vue.prototype.$store = store;
}
};
Vue.prototype.$store = store
},
}

View File

@@ -1,97 +1,103 @@
export default {
setState({ request }, { attribute, value }) {
request[attribute] = value;
request[attribute] = value
},
setGQLState({ gql }, { attribute, value }) {
gql[attribute] = value;
gql[attribute] = value
},
setCollapsedSection({ theme }, value) {
theme.collapsedSections.includes(value)
? (theme.collapsedSections = theme.collapsedSections.filter(section => section !== value))
: theme.collapsedSections.push(value)
},
addGQLHeader({ gql }, object) {
gql.headers.push(object);
gql.headers.push(object)
},
removeGQLHeader({ gql }, index) {
gql.headers.splice(index, 1);
gql.headers.splice(index, 1)
},
setGQLHeaderKey({ gql }, { index, value }) {
gql.headers[index].key = value;
gql.headers[index].key = value
},
setGQLHeaderValue({ gql }, { index, value }) {
gql.headers[index].value = value;
gql.headers[index].value = value
},
addHeaders({ request }, value) {
request.headers.push(value);
request.headers.push(value)
},
removeHeaders({ request }, index) {
request.headers.splice(index, 1);
request.headers.splice(index, 1)
},
setKeyHeader({ request }, { index, value }) {
request.headers[index].key = value;
request.headers[index].key = value
},
setValueHeader({ request }, { index, value }) {
request.headers[index].value = value;
request.headers[index].value = value
},
addParams({ request }, value) {
request.params.push(value);
request.params.push(value)
},
removeParams({ request }, index) {
request.params.splice(index, 1);
request.params.splice(index, 1)
},
setKeyParams({ request }, { index, value }) {
request.params[index].key = value;
request.params[index].key = value
},
setValueParams({ request }, { index, value }) {
request.params[index].value = value;
request.params[index].value = value
},
addBodyParams({ request }, value) {
request.bodyParams.push(value);
request.bodyParams.push(value)
},
removeBodyParams({ request }, index) {
request.bodyParams.splice(index, 1);
request.bodyParams.splice(index, 1)
},
setKeyBodyParams({ request }, { index, value }) {
request.bodyParams[index].key = value;
request.bodyParams[index].key = value
},
setValueBodyParams({ request }, { index, value }) {
request.bodyParams[index].value = value;
request.bodyParams[index].value = value
},
setOAuth2({ oauth2 }, { attribute, value }) {
oauth2[attribute] = value;
oauth2[attribute] = value
},
addOAuthToken({ oauth2 }, value) {
oauth2.tokens.push(value);
oauth2.tokens.push(value)
},
removeOAuthToken({ oauth2 }, index) {
oauth2.tokens.splice(index, 1);
oauth2.tokens.splice(index, 1)
},
setOAuthTokenName({ oauth2 }, { index, value }) {
oauth2.tokens[index].name = value;
oauth2.tokens[index].name = value
},
addOAuthTokenReq({ oauth2 }, value) {
oauth2.tokenReqs.push(value);
oauth2.tokenReqs.push(value)
},
removeOAuthTokenReq({ oauth2 }, index) {
oauth2.tokenReqs.splice(index, 1);
}
};
oauth2.tokenReqs.splice(index, 1)
},
}

View File

@@ -1,4 +1,4 @@
import Vue from "vue";
import Vue from "vue"
export const SETTINGS_KEYS = [
/**
@@ -68,8 +68,8 @@ export const SETTINGS_KEYS = [
* A boolean value indicating whether to use the browser extensions
* to run the requests
*/
"EXTENSIONS_ENABLED"
];
"EXTENSIONS_ENABLED",
]
export const state = () => ({
settings: {},
@@ -77,200 +77,190 @@ export const state = () => ({
{
name: "My Collection",
folders: [],
requests: []
}
requests: [],
},
],
environments: [
{
name: "My Environment Variables",
variables: []
}
variables: [],
},
],
editingEnvironment: {},
selectedRequest: {},
editingRequest: {}
});
editingRequest: {},
})
export const mutations = {
applySetting({ settings }, setting) {
if (
setting === null ||
!(setting instanceof Array) ||
setting.length !== 2
) {
throw new Error(
"You must provide a setting (array in the form [key, value])"
);
if (setting === null || !(setting instanceof Array) || setting.length !== 2) {
throw new Error("You must provide a setting (array in the form [key, value])")
}
const [key, value] = setting;
const [key, value] = setting
// Do not just remove this check.
// Add your settings key to the SETTINGS_KEYS array at the
// top of the file.
// This is to ensure that application settings remain documented.
if (!SETTINGS_KEYS.includes(key)) {
throw new Error(`The settings structure does not include the key ${key}`);
throw new Error(`The settings structure does not include the key ${key}`)
}
settings[key] = value;
settings[key] = value
},
removeVariables({ editingEnvironment }, value) {
editingEnvironment.variables = value;
editingEnvironment.variables = value
},
setEditingEnvironment(state, value) {
state.editingEnvironment = { ...value };
state.editingEnvironment = { ...value }
},
setVariableKey({ editingEnvironment }, { index, value }) {
editingEnvironment.variables[index].key = value;
editingEnvironment.variables[index].key = value
},
setVariableValue({ editingEnvironment }, { index, value }) {
editingEnvironment.variables[index].value = testValue(value);
editingEnvironment.variables[index].value = testValue(value)
},
removeVariable({ editingEnvironment }, variables) {
editingEnvironment.variables = variables;
editingEnvironment.variables = variables
},
addVariable({ editingEnvironment }, value) {
editingEnvironment.variables.push(value);
editingEnvironment.variables.push(value)
},
replaceEnvironments(state, environments) {
state.environments = environments;
state.environments = environments
},
importAddEnvironments(state, { environments, confirmation }) {
const duplicateEnvironment = environments.some(
item => {
return state.environments.some(
item2 => {
return item.name.toLowerCase() === item2.name.toLowerCase();
});
}
);
const duplicateEnvironment = environments.some(item => {
return state.environments.some(item2 => {
return item.name.toLowerCase() === item2.name.toLowerCase()
})
})
if (duplicateEnvironment) {
this.$toast.info("Duplicate environment");
return;
};
state.environments = [...state.environments, ...environments];
this.$toast.info("Duplicate environment")
return
}
state.environments = [...state.environments, ...environments]
let index = 0;
let index = 0
for (let environment of state.environments) {
environment.environmentIndex = index;
index += 1;
environment.environmentIndex = index
index += 1
}
this.$toast.info(confirmation, {
icon: "folder_shared"
});
icon: "folder_shared",
})
},
removeEnvironment({ environments }, environmentIndex) {
environments.splice(environmentIndex, 1);
environments.splice(environmentIndex, 1)
},
saveEnvironment({ environments }, payload) {
const { environment, environmentIndex } = payload;
const { name } = environment;
const duplicateEnvironment = environments.length === 1
? false
: environments.some(
item =>
item.environmentIndex !== environmentIndex &&
item.name.toLowerCase() === name.toLowerCase()
);
const { environment, environmentIndex } = payload
const { name } = environment
const duplicateEnvironment =
environments.length === 1
? false
: environments.some(
item =>
item.environmentIndex !== environmentIndex &&
item.name.toLowerCase() === name.toLowerCase()
)
if (duplicateEnvironment) {
this.$toast.info("Duplicate environment");
return;
this.$toast.info("Duplicate environment")
return
}
environments[environmentIndex] = environment;
environments[environmentIndex] = environment
},
replaceCollections(state, collections) {
state.collections = collections;
state.collections = collections
},
importCollections(state, collections) {
state.collections = [...state.collections, ...collections];
state.collections = [...state.collections, ...collections]
let index = 0;
let index = 0
for (let collection of collections) {
collection.collectionIndex = index;
index += 1;
collection.collectionIndex = index
index += 1
}
},
addNewCollection({ collections }, collection) {
const { name } = collection;
const { name } = collection
const duplicateCollection = collections.some(
item => item.name.toLowerCase() === name.toLowerCase()
);
)
if (duplicateCollection) {
this.$toast.info("Duplicate collection");
return;
this.$toast.info("Duplicate collection")
return
}
collections.push({
name: "",
folders: [],
requests: [],
...collection
});
...collection,
})
},
removeCollection({ collections }, payload) {
const { collectionIndex } = payload;
collections.splice(collectionIndex, 1);
const { collectionIndex } = payload
collections.splice(collectionIndex, 1)
},
editCollection({ collections }, payload) {
const {
collection: { name },
collectionIndex
} = payload;
collectionIndex,
} = payload
const duplicateCollection = collections.some(
item => item.name.toLowerCase() === name.toLowerCase()
);
)
if (duplicateCollection) {
this.$toast.info("Duplicate collection");
return;
this.$toast.info("Duplicate collection")
return
}
collections[collectionIndex] = collection;
collections[collectionIndex] = collection
},
addNewFolder({ collections }, payload) {
const { collectionIndex, folder } = payload;
const { collectionIndex, folder } = payload
collections[collectionIndex].folders.push({
name: "",
requests: [],
...folder
});
...folder,
})
},
editFolder({ collections }, payload) {
const { collectionIndex, folder, folderIndex } = payload;
Vue.set(collections[collectionIndex].folders, folderIndex, folder);
const { collectionIndex, folder, folderIndex } = payload
Vue.set(collections[collectionIndex].folders, folderIndex, folder)
},
removeFolder({ collections }, payload) {
const { collectionIndex, folderIndex } = payload;
collections[collectionIndex].folders.splice(folderIndex, 1);
const { collectionIndex, folderIndex } = payload
collections[collectionIndex].folders.splice(folderIndex, 1)
},
addRequest({ collections }, payload) {
const { request } = payload;
const { request } = payload
// Request that is directly attached to collection
if (request.folder === -1) {
collections[request.collection].requests.push(request);
return;
collections[request.collection].requests.push(request)
return
}
collections[request.collection].folders[request.folder].requests.push(
request
);
collections[request.collection].folders[request.folder].requests.push(request)
},
editRequest({ collections }, payload) {
@@ -280,148 +270,116 @@ export const mutations = {
requestOldIndex,
requestNew,
requestNewCollectionIndex,
requestNewFolderIndex
} = payload;
requestNewFolderIndex,
} = payload
const changedCollection =
requestOldCollectionIndex !== requestNewCollectionIndex;
const changedFolder = requestOldFolderIndex !== requestNewFolderIndex;
const changedPlace = changedCollection || changedFolder;
const changedCollection = requestOldCollectionIndex !== requestNewCollectionIndex
const changedFolder = requestOldFolderIndex !== requestNewFolderIndex
const changedPlace = changedCollection || changedFolder
// set new request
if (requestNewFolderIndex !== undefined) {
Vue.set(
collections[requestNewCollectionIndex].folders[requestNewFolderIndex]
.requests,
collections[requestNewCollectionIndex].folders[requestNewFolderIndex].requests,
requestOldIndex,
requestNew
);
)
} else {
Vue.set(
collections[requestNewCollectionIndex].requests,
requestOldIndex,
requestNew
);
Vue.set(collections[requestNewCollectionIndex].requests, requestOldIndex, requestNew)
}
// remove old request
if (changedPlace) {
if (requestOldFolderIndex !== undefined) {
collections[requestOldCollectionIndex].folders[
requestOldFolderIndex
].requests.splice(requestOldIndex, 1);
} else {
collections[requestOldCollectionIndex].requests.splice(
collections[requestOldCollectionIndex].folders[requestOldFolderIndex].requests.splice(
requestOldIndex,
1
);
)
} else {
collections[requestOldCollectionIndex].requests.splice(requestOldIndex, 1)
}
}
},
saveRequestAs({ collections }, payload) {
const { request, collectionIndex, folderIndex, requestIndex } = payload;
const { request, collectionIndex, folderIndex, requestIndex } = payload
const specifiedCollection = collectionIndex !== undefined;
const specifiedFolder = folderIndex !== undefined;
const specifiedRequest = requestIndex !== undefined;
const specifiedCollection = collectionIndex !== undefined
const specifiedFolder = folderIndex !== undefined
const specifiedRequest = requestIndex !== undefined
if (specifiedCollection && specifiedFolder && specifiedRequest) {
Vue.set(
collections[collectionIndex].folders[folderIndex].requests,
requestIndex,
request
);
Vue.set(collections[collectionIndex].folders[folderIndex].requests, requestIndex, request)
} else if (specifiedCollection && specifiedFolder && !specifiedRequest) {
const requests =
collections[collectionIndex].folders[folderIndex].requests;
const lastRequestIndex = requests.length - 1;
Vue.set(requests, lastRequestIndex + 1, request);
const requests = collections[collectionIndex].folders[folderIndex].requests
const lastRequestIndex = requests.length - 1
Vue.set(requests, lastRequestIndex + 1, request)
} else if (specifiedCollection && !specifiedFolder && specifiedRequest) {
const requests = collections[collectionIndex].requests;
Vue.set(requests, requestIndex, request);
const requests = collections[collectionIndex].requests
Vue.set(requests, requestIndex, request)
} else if (specifiedCollection && !specifiedFolder && !specifiedRequest) {
const requests = collections[collectionIndex].requests;
const lastRequestIndex = requests.length - 1;
Vue.set(requests, lastRequestIndex + 1, request);
const requests = collections[collectionIndex].requests
const lastRequestIndex = requests.length - 1
Vue.set(requests, lastRequestIndex + 1, request)
}
},
saveRequest({ collections }, payload) {
const { request } = payload;
const { request } = payload
// Remove the old request from collection
if (request.hasOwnProperty("oldCollection") && request.oldCollection > -1) {
const folder =
request.hasOwnProperty("oldFolder") && request.oldFolder >= -1
? request.oldFolder
: request.folder;
: request.folder
if (folder > -1) {
collections[request.oldCollection].folders[folder].requests.splice(
request.requestIndex,
1
);
collections[request.oldCollection].folders[folder].requests.splice(request.requestIndex, 1)
} else {
collections[request.oldCollection].requests.splice(
request.requestIndex,
1
);
collections[request.oldCollection].requests.splice(request.requestIndex, 1)
}
} else if (
request.hasOwnProperty("oldFolder") &&
request.oldFolder !== -1
) {
collections[request.collection].folders[folder].requests.splice(
request.requestIndex,
1
);
} else if (request.hasOwnProperty("oldFolder") && request.oldFolder !== -1) {
collections[request.collection].folders[folder].requests.splice(request.requestIndex, 1)
}
delete request.oldCollection;
delete request.oldFolder;
delete request.oldCollection
delete request.oldFolder
// Request that is directly attached to collection
if (request.folder === -1) {
Vue.set(
collections[request.collection].requests,
request.requestIndex,
request
);
return;
Vue.set(collections[request.collection].requests, request.requestIndex, request)
return
}
Vue.set(
collections[request.collection].folders[request.folder].requests,
request.requestIndex,
request
);
)
},
removeRequest({ collections }, payload) {
const { collectionIndex, folderIndex, requestIndex } = payload;
const { collectionIndex, folderIndex, requestIndex } = payload
// Request that is directly attached to collection
if (folderIndex === -1) {
collections[collectionIndex].requests.splice(requestIndex, 1);
return;
collections[collectionIndex].requests.splice(requestIndex, 1)
return
}
collections[collectionIndex].folders[folderIndex].requests.splice(
requestIndex,
1
);
collections[collectionIndex].folders[folderIndex].requests.splice(requestIndex, 1)
},
selectRequest(state, { request }) {
state.selectedRequest = Object.assign({}, request);
}
};
state.selectedRequest = Object.assign({}, request)
},
}
function testValue(myValue) {
try {
return JSON.parse(myValue);
} catch(ex) {
// Now we know it's a string just leave it as a string value.
return myValue;
return JSON.parse(myValue)
} catch (ex) {
// Now we know it's a string just leave it as a string value.
return myValue
}
}

View File

@@ -1,6 +1,7 @@
export default () => ({
request: {
method: "GET",
uri: "",
url: "https://httpbin.org",
path: "/get",
label: "",
@@ -15,13 +16,18 @@ export default () => ({
rawParams: "",
rawInput: false,
requestType: "",
contentType: ""
contentType: "",
},
gql: {
url: "https://rickandmortyapi.com/graphql",
headers: [],
schema: "",
variablesJSONString: "{}",
query: ""
query: "",
response: "",
},
theme: {
collapsedSections: [],
},
oauth2: {
tokens: [],
@@ -33,6 +39,6 @@ export default () => ({
authUrl: "",
accessTokenUrl: "",
clientId: "",
scope: ""
}
});
scope: "",
},
})

View File

@@ -1 +1 @@
{ "message": "FAKE Cat API" }
{ "message": "FAKE Cat API" }

View File

@@ -1,7 +1,7 @@
describe('Visit home', () => {
describe("Visit home", () => {
it('Have a page title with "Postwoman"', () => {
cy.visit('/', { retryOnStatusCodeFailure: true })
.get('title')
.should('contain','Postwoman')
cy.visit("/", { retryOnStatusCodeFailure: true })
.get("title")
.should("contain", "Postwoman")
})
})

View File

@@ -1,34 +1,34 @@
describe('Authentication', () => {
describe("Authentication", () => {
it(`Change default auth user and pass with url`, () => {
cy.visit(`?&auth=Basic Auth&httpUser=foo&httpPassword=bar`, { retryOnStatusCodeFailure: true })
.get('input[name="http_basic_user"]', { timeout: 500 })
.invoke('val')
.then((user) => {
expect(user === 'foo').to.equal(true)
.invoke("val")
.then(user => {
expect(user === "foo").to.equal(true)
})
.get('input[name="http_basic_passwd"]')
.invoke('val')
.then((pass) => {
expect(pass === 'bar').to.equal(true)
.invoke("val")
.then(pass => {
expect(pass === "bar").to.equal(true)
})
})
it('Enable user and pass at url with toggler', () => {
cy.visit('/', { retryOnStatusCodeFailure: true })
.get('#auth')
.select('Basic Auth')
it("Enable user and pass at url with toggler", () => {
cy.visit("/", { retryOnStatusCodeFailure: true })
.get("#auth")
.select("Basic Auth")
.get('input[name="http_basic_user"]', { timeout: 500 })
.type('foo')
.type("foo")
.get('input[name="http_basic_passwd"]', { timeout: 500 })
.type('bar')
.type("bar")
.url()
.should('not.contain', 'foo')
.should('not.contain', 'bar')
.get('.toggle')
.should("not.contain", "foo")
.should("not.contain", "bar")
.get(".toggle")
.click()
.url()
.should('contain', 'foo')
.should('contain', 'bar')
})
.should("contain", "foo")
.should("contain", "bar")
})
})

View File

@@ -1,20 +1,23 @@
describe('Proxy disabled - local request', () => {
it('Change default url with query and make a request to local cat api', () => {
cy.seedAndVisit('catapi', '/?url=https://api.thecatapi.com&path=')
.get('#url').then((el) => expect(el.val() === 'https://api.thecatapi.com').to.equal(true))
.get("#path").then((el) => expect(el.val() === '').to.equal(true))
.get('#response-details-wrapper').should($wrapper => {
expect($wrapper).to.contain('FAKE Cat API')
describe("Proxy disabled - local request", () => {
it("Change default url with query and make a request to local cat api", () => {
cy.seedAndVisit("catapi", "/?url=https://api.thecatapi.com&path=")
.get("#url")
.then(el => expect(el.val() === "https://api.thecatapi.com").to.equal(true))
.get("#response-details-wrapper")
.should($wrapper => {
expect($wrapper).to.contain("FAKE Cat API")
})
})
})
describe('Proxy enabled - external request', () => {
it('Enable the proxy and make a request to the real cat api', () => {
cy.enableProxy('/?url=https://api.thecatapi.com&path=')
.get('#send').click()
.get('#response-details-wrapper').should($wrapper => {
expect($wrapper).to.contain('Cat API')
describe("Proxy enabled - external request", () => {
it("Enable the proxy and make a request to the real cat api", () => {
cy.enableProxy("/?url=https://api.thecatapi.com&path=")
.get("#send")
.click()
.get("#response-details-wrapper")
.should($wrapper => {
expect($wrapper).to.contain("Cat API")
})
})
})

View File

@@ -1,29 +1,31 @@
/**
* Creates cy.seedAndVisit() function
* This function will go to some path and wait for some fake response from 'src/tests/fixtures/*.json'
* @param { String } seedData The name of json at 'src/tests/fixtures/
* @param { String } path The path or query parameters to go -ex. '/?path=/api/users'
* @param { String } method The fake request method
*/
Cypress.Commands.add('seedAndVisit', (seedData, path = '/', method = 'GET') => {
* Creates cy.seedAndVisit() function
* This function will go to some path and wait for some fake response from 'src/tests/fixtures/*.json'
* @param { String } seedData The name of json at 'src/tests/fixtures/
* @param { String } path The path or query parameters to go -ex. '/?path=/api/users'
* @param { String } method The fake request method
*/
Cypress.Commands.add("seedAndVisit", (seedData, path = "/", method = "GET") => {
cy.server()
.route(method, 'https://api.thecatapi.com/', `fixture:${seedData}`).as('load')
.route(method, "https://api.thecatapi.com/", `fixture:${seedData}`)
.as("load")
cy.visit(path)
.get('#send').click()
.wait('@load')
.get("#send")
.click()
.wait("@load")
})
/**
* Creates cy.enableProxy() function
* This function will enable the proxy and navigate back to a given path
* @param { String } goBackPath The page go back
*/
Cypress.Commands.add('enableProxy', (goBackPath) => {
cy.visit('/settings')
.get('#proxy')
.find('.toggle')
.click( { force: true } )
.should('have.class', 'on')
* Creates cy.enableProxy() function
* This function will enable the proxy and navigate back to a given path
* @param { String } goBackPath The page go back
*/
Cypress.Commands.add("enableProxy", goBackPath => {
cy.visit("/settings")
.get("#proxy")
.find(".toggle")
.click({ force: true })
.should("have.class", "on")
.visit(goBackPath)
})
})

View File

@@ -1 +1 @@
import './commands'
import "./commands"