Merge branch 'newstate/environments' into main

This commit is contained in:
Andrew Bastin
2021-06-06 23:12:41 -04:00
9 changed files with 452 additions and 223 deletions

View File

@@ -36,48 +36,26 @@
</SmartModal> </SmartModal>
</template> </template>
<script> <script lang="ts">
import { fb } from "~/helpers/fb" import Vue from "vue"
import { getSettingSubject } from "~/newstore/settings" import { createEnvironment } from "~/newstore/environments"
export default { export default Vue.extend({
props: { props: {
show: Boolean, show: Boolean,
}, },
data() { data() {
return { return {
name: null, name: null as string | null,
}
},
subscriptions() {
return {
SYNC_ENVIRONMENTS: getSettingSubject("syncEnvironments"),
} }
}, },
methods: { methods: {
syncEnvironments() {
if (fb.currentUser !== null && this.SYNC_ENVIRONMENTS) {
fb.writeEnvironments(
JSON.parse(JSON.stringify(this.$store.state.postwoman.environments))
)
}
},
addNewEnvironment() { addNewEnvironment() {
if (!this.$data.name) { if (!this.name) {
this.$toast.info(this.$t("invalid_environment_name")) this.$toast.info(this.$t("invalid_environment_name").toString())
return return
} }
const newEnvironment = [ createEnvironment(this.name)
{
name: this.$data.name,
variables: [],
},
]
this.$store.commit("postwoman/importAddEnvironments", {
environments: newEnvironment,
confirmation: "Environment added",
})
this.syncEnvironments()
this.hideModal() this.hideModal()
}, },
hideModal() { hideModal() {
@@ -85,5 +63,5 @@ export default {
this.$emit("hide-modal") this.$emit("hide-modal")
}, },
}, },
} })
</script> </script>

View File

@@ -32,7 +32,7 @@
</div> </div>
</div> </div>
<ul <ul
v-for="(variable, index) in editingEnvCopy.variables" v-for="(variable, index) in vars"
:key="index" :key="index"
class=" class="
border-b border-dashed border-b border-dashed
@@ -46,33 +46,16 @@
> >
<li> <li>
<input <input
v-model="variable.key"
:placeholder="$t('variable_count', { count: index + 1 })" :placeholder="$t('variable_count', { count: index + 1 })"
:name="'param' + index" :name="'param' + index"
:value="variable.key"
autofocus
@change="
$store.commit('postwoman/setVariableKey', {
index,
value: $event.target.value,
})
"
/> />
</li> </li>
<li> <li>
<input <input
v-model="variable.value"
:placeholder="$t('value_count', { count: index + 1 })" :placeholder="$t('value_count', { count: index + 1 })"
:name="'value' + index" :name="'value' + index"
:value="
typeof variable.value === 'string'
? variable.value
: JSON.stringify(variable.value)
"
@change="
$store.commit('postwoman/setVariableValue', {
index,
value: $event.target.value,
})
"
/> />
</li> </li>
<div> <div>
@@ -113,60 +96,44 @@
</SmartModal> </SmartModal>
</template> </template>
<script> <script lang="ts">
import { fb } from "~/helpers/fb" import Vue, { PropType } from "vue"
import { getSettingSubject } from "~/newstore/settings" import clone from "lodash/clone"
import type { Environment } from "~/newstore/environments"
import { updateEnvironment } from "~/newstore/environments"
export default { export default Vue.extend({
props: { props: {
show: Boolean, show: Boolean,
editingEnvironment: { type: Object, default: () => {} }, editingEnvironment: {
type: Object as PropType<Environment | null>,
default: null,
},
editingEnvironmentIndex: { type: Number, default: null }, editingEnvironmentIndex: { type: Number, default: null },
}, },
data() { data() {
return { return {
name: null, name: null as string | null,
vars: [] as { key: string; value: string }[],
doneButton: '<i class="material-icons">done</i>', doneButton: '<i class="material-icons">done</i>',
} }
}, },
subscriptions() {
return {
SYNC_ENVIRONMENTS: getSettingSubject("syncEnvironments"),
}
},
computed: {
editingEnvCopy() {
return this.$store.state.postwoman.editingEnvironment
},
variableString() {
const result = this.editingEnvCopy.variables
return result === "" ? "" : JSON.stringify(result)
},
},
watch: { watch: {
editingEnvironment() { editingEnvironment() {
this.name = this.name = this.editingEnvironment?.name ?? null
this.$props.editingEnvironment && this.$props.editingEnvironment.name this.vars = clone(this.editingEnvironment?.variables ?? [])
? this.$props.editingEnvironment.name },
: undefined show() {
this.$store.commit( this.name = this.editingEnvironment?.name ?? null
"postwoman/setEditingEnvironment", this.vars = clone(this.editingEnvironment?.variables ?? [])
this.$props.editingEnvironment
)
}, },
}, },
methods: { methods: {
syncEnvironments() { clearContent({ target }: { target: HTMLElement }) {
if (fb.currentUser !== null && this.SYNC_ENVIRONMENTS) { this.vars = []
fb.writeEnvironments(
JSON.parse(JSON.stringify(this.$store.state.postwoman.environments))
)
}
},
clearContent({ target }) {
this.$store.commit("postwoman/removeVariables", [])
target.innerHTML = this.doneButton target.innerHTML = this.doneButton
this.$toast.info(this.$t("cleared"), { this.$toast.info(this.$t("cleared").toString(), {
icon: "clear_all", icon: "clear_all",
}) })
setTimeout( setTimeout(
@@ -175,44 +142,26 @@ export default {
) )
}, },
addEnvironmentVariable() { addEnvironmentVariable() {
const value = { key: "", value: "" } this.vars.push({
this.$store.commit("postwoman/addVariable", value) key: "",
this.syncEnvironments() value: "",
},
removeEnvironmentVariable(index) {
const variableIndex = index
const oldVariables = this.editingEnvCopy.variables.slice()
const newVariables = this.editingEnvCopy.variables.filter(
(_, index) => variableIndex !== index
)
this.$store.commit("postwoman/removeVariable", newVariables)
this.$toast.error(this.$t("deleted"), {
icon: "delete",
action: {
text: this.$t("undo"),
onClick: (_, toastObject) => {
this.$store.commit("postwoman/removeVariable", oldVariables)
toastObject.remove()
},
},
}) })
this.syncEnvironments() },
removeEnvironmentVariable(index: number) {
this.vars.splice(index, 1)
}, },
saveEnvironment() { saveEnvironment() {
if (!this.$data.name) { if (!this.name) {
this.$toast.info(this.$t("invalid_environment_name")) this.$toast.info(this.$t("invalid_environment_name").toString())
return return
} }
const environmentUpdated = {
...this.editingEnvCopy, const environmentUpdated: Environment = {
name: this.$data.name, name: this.name,
variables: this.vars,
} }
this.$store.commit("postwoman/saveEnvironment", {
environment: environmentUpdated, updateEnvironment(this.editingEnvironmentIndex, environmentUpdated)
environmentIndex: this.$props.editingEnvironmentIndex,
})
this.syncEnvironments()
this.hideModal() this.hideModal()
}, },
hideModal() { hideModal() {
@@ -220,5 +169,5 @@ export default {
this.$emit("hide-modal") this.$emit("hide-modal")
}, },
}, },
} })
</script> </script>

View File

@@ -40,11 +40,11 @@
</div> </div>
</template> </template>
<script> <script lang="ts">
import { fb } from "~/helpers/fb" import Vue from "vue"
import { getSettingSubject } from "~/newstore/settings" import { deleteEnvironment } from "~/newstore/environments"
export default { export default Vue.extend({
props: { props: {
environment: { type: Object, default: () => {} }, environment: { type: Object, default: () => {} },
environmentIndex: { type: Number, default: null }, environmentIndex: { type: Number, default: null },
@@ -54,26 +54,13 @@ export default {
confirmRemove: false, confirmRemove: false,
} }
}, },
subscriptions() {
return {
SYNC_ENVIRONMENTS: getSettingSubject("syncEnvironments"),
}
},
methods: { methods: {
syncEnvironments() {
if (fb.currentUser !== null && this.SYNC_ENVIRONMENTS) {
fb.writeEnvironments(
JSON.parse(JSON.stringify(this.$store.state.postwoman.environments))
)
}
},
removeEnvironment() { removeEnvironment() {
this.$store.commit("postwoman/removeEnvironment", this.environmentIndex) deleteEnvironment(this.environmentIndex)
this.$toast.error(this.$t("deleted"), { this.$toast.error(this.$t("deleted").toString(), {
icon: "delete", icon: "delete",
}) })
this.syncEnvironments()
}, },
}, },
} })
</script> </script>

View File

@@ -75,8 +75,11 @@
</template> </template>
<script> <script>
import { fb } from "~/helpers/fb" import {
import { getSettingSubject } from "~/newstore/settings" environments$,
setCurrentEnvironment,
selectedEnvIndex$,
} from "~/newstore/environments"
export default { export default {
data() { data() {
@@ -87,52 +90,17 @@ export default {
editingEnvironment: undefined, editingEnvironment: undefined,
editingEnvironmentIndex: undefined, editingEnvironmentIndex: undefined,
selectedEnvironmentIndex: -1, selectedEnvironmentIndex: -1,
defaultEnvironment: {
name: "My Environment Variables",
variables: [],
},
} }
}, },
subscriptions() { subscriptions() {
return { return {
SYNC_ENVIRONMENTS: getSettingSubject("syncEnvironments"), environments: environments$,
selectedEnvironmentIndex: selectedEnvIndex$,
} }
}, },
computed: {
environments() {
return fb.currentUser !== null
? fb.currentEnvironments
: this.$store.state.postwoman.environments
},
},
watch: { watch: {
selectedEnvironmentIndex(val) { selectedEnvironmentIndex(val) {
if (val === -1) setCurrentEnvironment(val)
this.$emit("use-environment", {
environment: this.defaultEnvironment,
environments: this.environments,
})
else
this.$emit("use-environment", {
environment: this.environments[val],
environments: this.environments,
})
},
environments: {
handler({ length }) {
if (length === 0) {
this.selectedEnvironmentIndex = -1
this.$emit("use-environment", {
environment: this.defaultEnvironment,
environments: this.environments,
})
} else if (this.environments[this.selectedEnvironmentIndex])
this.$emit("use-environment", {
environment: this.environments[this.selectedEnvironmentIndex],
environments: this.environments,
})
else this.selectedEnvironmentIndex = -1
},
}, },
}, },
mounted() { mounted() {
@@ -166,19 +134,11 @@ export default {
this.$data.editingEnvironment = environment this.$data.editingEnvironment = environment
this.$data.editingEnvironmentIndex = environmentIndex this.$data.editingEnvironmentIndex = environmentIndex
this.displayModalEdit(true) this.displayModalEdit(true)
this.syncEnvironments()
}, },
resetSelectedData() { resetSelectedData() {
this.$data.editingEnvironment = undefined this.$data.editingEnvironment = undefined
this.$data.editingEnvironmentIndex = undefined this.$data.editingEnvironmentIndex = undefined
}, },
syncEnvironments() {
if (fb.currentUser !== null && this.SYNC_ENVIRONMENTS) {
fb.writeEnvironments(
JSON.parse(JSON.stringify(this.$store.state.postwoman.environments))
)
}
},
}, },
} }
</script> </script>

View File

@@ -16,6 +16,7 @@ import {
graphqlCollectionStore, graphqlCollectionStore,
setGraphqlCollections, setGraphqlCollections,
} from "~/newstore/collections" } from "~/newstore/collections"
import { environments$, replaceEnvironments } from "~/newstore/environments"
// Initialize Firebase, copied from cloud console // Initialize Firebase, copied from cloud console
const firebaseConfig = { const firebaseConfig = {
@@ -45,7 +46,6 @@ export class FirebaseInstance {
this.idToken = null this.idToken = null
this.currentFeeds = [] this.currentFeeds = []
this.currentSettings = [] this.currentSettings = []
this.currentEnvironments = []
this.currentUser$ = new ReplaySubject(1) this.currentUser$ = new ReplaySubject(1)
this.idToken$ = new ReplaySubject(1) this.idToken$ = new ReplaySubject(1)
@@ -55,6 +55,7 @@ export class FirebaseInstance {
let loadedGraphqlHistory = false let loadedGraphqlHistory = false
let loadedRESTCollections = false let loadedRESTCollections = false
let loadedGraphqlCollections = false let loadedGraphqlCollections = false
let loadedEnvironments = false
graphqlCollectionStore.subject$.subscribe(({ state }) => { graphqlCollectionStore.subject$.subscribe(({ state }) => {
if ( if (
@@ -113,7 +114,7 @@ export class FirebaseInstance {
}) })
settingsStore.dispatches$.subscribe((dispatch) => { settingsStore.dispatches$.subscribe((dispatch) => {
if (this.currentSettings && loadedSettings) { if (this.currentUser && loadedSettings) {
if (dispatch.dispatcher === "bulkApplySettings") { if (dispatch.dispatcher === "bulkApplySettings") {
Object.keys(dispatch.payload).forEach((key) => { Object.keys(dispatch.payload).forEach((key) => {
this.writeSettings(key, dispatch.payload[key]) this.writeSettings(key, dispatch.payload[key])
@@ -127,6 +128,12 @@ export class FirebaseInstance {
} }
}) })
environments$.subscribe((envs) => {
if (this.currentUser && loadedEnvironments) {
this.writeEnvironments(envs)
}
})
this.app.auth().onIdTokenChanged((user) => { this.app.auth().onIdTokenChanged((user) => {
if (user) { if (user) {
user.getIdToken().then((token) => { user.getIdToken().then((token) => {
@@ -292,9 +299,9 @@ export class FirebaseInstance {
environment.id = doc.id environment.id = doc.id
environments.push(environment) environments.push(environment)
}) })
if (environments.length > 0) { loadedEnvironments = false
this.currentEnvironments = environments[0].environment replaceEnvironments(environments[0].environment)
} loadedEnvironments = true
}) })
} else { } else {
this.currentUser = null this.currentUser = null

View File

@@ -1,15 +1,34 @@
export default function getEnvironmentVariablesFromScript(script) { import {
const _variables = {} getCurrentEnvironment,
getGlobalEnvironment,
} from "~/newstore/environments"
export default function getEnvironmentVariablesFromScript(script: any) {
const _variables: Record<string, string> = {}
const currentEnv = getCurrentEnvironment()
for (const variable of currentEnv.variables) {
_variables[variable.key] = variable.value
}
const globalEnv = getGlobalEnvironment()
if (globalEnv) {
for (const variable of globalEnv.variables) {
_variables[variable.key] = variable.value
}
}
try { try {
// the pw object is the proxy by which pre-request scripts can pass variables to the request. // 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. // for security and control purposes, this is the only way a pre-request script should modify variables.
const pw = { const pw = {
environment: { environment: {
set: (key, value) => (_variables[key] = value), set: (key: string, value: string) => (_variables[key] = value),
}, },
env: { env: {
set: (key, value) => (_variables[key] = value), set: (key: string, value: string) => (_variables[key] = value),
}, },
// globals that the script is allowed to have access to. // globals that the script is allowed to have access to.
} }

330
newstore/environments.ts Normal file
View File

@@ -0,0 +1,330 @@
import { pluck } from "rxjs/operators"
import DispatchingStore, {
defineDispatchers,
} from "~/newstore/DispatchingStore"
export type Environment = {
name: string
variables: {
key: string
value: string
}[]
}
const defaultEnvironmentsState = {
environments: [
{
name: "My Environment Variables",
variables: [],
},
] as Environment[],
// Current environment index specifies the index
// -1 means no environments are selected
currentEnvironmentIndex: -1,
}
type EnvironmentStore = typeof defaultEnvironmentsState
const dispatchers = defineDispatchers({
setCurrentEnviromentIndex(
{ environments }: EnvironmentStore,
{ newIndex }: { newIndex: number }
) {
if (newIndex >= environments.length || newIndex <= -2) {
console.log(
`Ignoring possibly invalid current environment index assignment (value: ${newIndex})`
)
return {}
}
return {
currentEnvironmentIndex: newIndex,
}
},
replaceEnvironments(
_: EnvironmentStore,
{ environments }: { environments: Environment[] }
) {
return {
environments,
}
},
createEnvironment(
{ environments }: EnvironmentStore,
{ name }: { name: string }
) {
return {
environments: [
...environments,
{
name,
variables: [],
},
],
}
},
deleteEnvironment(
{ environments, currentEnvironmentIndex }: EnvironmentStore,
{ envIndex }: { envIndex: number }
) {
return {
environments: environments.filter((_, index) => index !== envIndex),
currentEnvironmentIndex:
envIndex === currentEnvironmentIndex ? -1 : currentEnvironmentIndex,
}
},
renameEnvironment(
{ environments }: EnvironmentStore,
{ envIndex, newName }: { envIndex: number; newName: string }
) {
return {
environments: environments.map((env, index) =>
index === envIndex
? {
...env,
name: newName,
}
: env
),
}
},
updateEnvironment(
{ environments }: EnvironmentStore,
{ envIndex, updatedEnv }: { envIndex: number; updatedEnv: Environment }
) {
return {
environments: environments.map((env, index) =>
index === envIndex ? updatedEnv : env
),
}
},
addEnvironmentVariable(
{ environments }: EnvironmentStore,
{ envIndex, key, value }: { envIndex: number; key: string; value: string }
) {
return {
environments: environments.map((env, index) =>
index === envIndex
? {
...env,
variables: [...env.variables, { key, value }],
}
: env
),
}
},
removeEnvironmentVariable(
{ environments }: EnvironmentStore,
{ envIndex, variableIndex }: { envIndex: number; variableIndex: number }
) {
return {
environments: environments.map((env, index) =>
index === envIndex
? {
...env,
variables: env.variables.filter(
(_, vIndex) => vIndex !== variableIndex
),
}
: env
),
}
},
setEnvironmentVariables(
{ environments }: EnvironmentStore,
{
envIndex,
vars,
}: { envIndex: number; vars: { key: string; value: string }[] }
) {
return {
environments: environments.map((env, index) =>
index === envIndex
? {
...env,
variables: vars,
}
: env
),
}
},
updateEnvironmentVariable(
{ environments }: EnvironmentStore,
{
envIndex,
variableIndex,
updatedKey,
updatedValue,
}: {
envIndex: number
variableIndex: number
updatedKey: string
updatedValue: string
}
) {
return {
environments: environments.map((env, index) =>
index === envIndex
? {
...env,
variables: env.variables.map((v, vIndex) =>
vIndex === variableIndex
? { key: updatedKey, value: updatedValue }
: v
),
}
: env
),
}
},
})
export const environmentsStore = new DispatchingStore(
defaultEnvironmentsState,
dispatchers
)
export const environments$ = environmentsStore.subject$.pipe(
pluck("environments")
)
export const selectedEnvIndex$ = environmentsStore.subject$.pipe(
pluck("currentEnvironmentIndex")
)
export function getCurrentEnvironment(): Environment {
if (environmentsStore.value.currentEnvironmentIndex === -1) {
return {
name: "No Environment",
variables: [],
}
}
return environmentsStore.value.environments[
environmentsStore.value.currentEnvironmentIndex
]
}
export function setCurrentEnvironment(newEnvIndex: number) {
environmentsStore.dispatch({
dispatcher: "setCurrentEnviromentIndex",
payload: {
newIndex: newEnvIndex,
},
})
}
export function getGlobalEnvironment(): Environment | null {
const envs = environmentsStore.value.environments
const el = envs.find(
(env) => env.name === "globals" || env.name === "Globals"
)
return el ?? null
}
export function replaceEnvironments(newEnvironments: any[]) {
environmentsStore.dispatch({
dispatcher: "replaceEnvironments",
payload: {
environments: newEnvironments,
},
})
}
export function createEnvironment(envName: string) {
environmentsStore.dispatch({
dispatcher: "createEnvironment",
payload: {
name: envName,
},
})
}
export function deleteEnvironment(envIndex: number) {
environmentsStore.dispatch({
dispatcher: "deleteEnvironment",
payload: {
envIndex,
},
})
}
export function renameEnvironment(envIndex: number, newName: string) {
environmentsStore.dispatch({
dispatcher: "renameEnvironment",
payload: {
envIndex,
newName,
},
})
}
export function updateEnvironment(envIndex: number, updatedEnv: Environment) {
environmentsStore.dispatch({
dispatcher: "updateEnvironment",
payload: {
envIndex,
updatedEnv,
},
})
}
export function setEnvironmentVariables(
envIndex: number,
vars: { key: string; value: string }[]
) {
environmentsStore.dispatch({
dispatcher: "setEnvironmentVariables",
payload: {
envIndex,
vars,
},
})
}
export function addEnvironmentVariable(
envIndex: number,
{ key, value }: { key: string; value: string }
) {
environmentsStore.dispatch({
dispatcher: "addEnvironmentVariable",
payload: {
envIndex,
key,
value,
},
})
}
export function removeEnvironmentVariable(
envIndex: number,
variableIndex: number
) {
environmentsStore.dispatch({
dispatcher: "removeEnvironmentVariable",
payload: {
envIndex,
variableIndex,
},
})
}
export function updateEnvironmentVariable(
envIndex: number,
variableIndex: number,
{ key, value }: { key: string; value: string }
) {
environmentsStore.dispatch({
dispatcher: "updateEnvironmentVariable",
payload: {
envIndex,
variableIndex,
updatedKey: key,
updatedValue: value,
},
})
}

View File

@@ -14,6 +14,7 @@ import {
setGraphqlCollections, setGraphqlCollections,
setRESTCollections, setRESTCollections,
} from "./collections" } from "./collections"
import { replaceEnvironments, environments$ } from "./environments"
function checkAndMigrateOldSettings() { function checkAndMigrateOldSettings() {
const vuexData = JSON.parse(window.localStorage.getItem("vuex") || "{}") const vuexData = JSON.parse(window.localStorage.getItem("vuex") || "{}")
@@ -44,6 +45,14 @@ function checkAndMigrateOldSettings() {
delete vuexData.postwoman.collectionsGraphql delete vuexData.postwoman.collectionsGraphql
window.localStorage.setItem("vuex", JSON.stringify(vuexData)) window.localStorage.setItem("vuex", JSON.stringify(vuexData))
} }
if (vuexData.postwoman && vuexData.postwoman.environments) {
const envs = vuexData.postwoman.environments
window.localStorage.setItem("environments", JSON.stringify(envs))
delete vuexData.postwoman.environments
window.localStorage.setItem("vuex", JSON.stringify(vuexData))
}
} }
function setupSettingsPersistence() { function setupSettingsPersistence() {
@@ -102,10 +111,23 @@ function setupCollectionsPersistence() {
}) })
} }
function setupEnvironmentsPersistence() {
const environmentsData = JSON.parse(
window.localStorage.getItem("environments") || "[]"
)
replaceEnvironments(environmentsData)
environments$.subscribe((envs) => {
window.localStorage.setItem("environments", JSON.stringify(envs))
})
}
export function setupLocalPersistence() { export function setupLocalPersistence() {
checkAndMigrateOldSettings() checkAndMigrateOldSettings()
setupSettingsPersistence() setupSettingsPersistence()
setupHistoryPersistence() setupHistoryPersistence()
setupCollectionsPersistence() setupCollectionsPersistence()
setupEnvironmentsPersistence()
} }

View File

@@ -567,7 +567,7 @@
</SmartTab> </SmartTab>
<SmartTab :id="'env'" :label="$t('environments')"> <SmartTab :id="'env'" :label="$t('environments')">
<Environments @use-environment="useSelectedEnvironment($event)" /> <Environments />
</SmartTab> </SmartTab>
<SmartTab :id="'notes'" :label="$t('notes')"> <SmartTab :id="'notes'" :label="$t('notes')">
@@ -1217,29 +1217,6 @@ export default {
}, },
}, },
methods: { methods: {
useSelectedEnvironment(args) {
let environment = args.environment
let environments = args.environments
let preRequestScriptString = ""
for (let variable of environment.variables) {
preRequestScriptString += `pw.env.set('${variable.key}', '${variable.value}');\n`
}
for (let env of environments) {
if (env.name === environment.name) {
continue
}
if (env.name === "Globals" || env.name === "globals") {
preRequestScriptString += this.useSelectedEnvironment({
environment: env,
environments,
})
}
}
this.preRequestScript = preRequestScriptString
this.showPreRequestScript = true
return preRequestScriptString
},
checkCollections() { checkCollections() {
const checkCollectionAvailability = const checkCollectionAvailability =
this.$store.state.postwoman.collections && this.$store.state.postwoman.collections &&