refactor: lint

This commit is contained in:
liyasthomas
2021-05-19 10:24:57 +05:30
parent 40ddfa8def
commit 44df9b3be8
20 changed files with 1080 additions and 690 deletions

View File

@@ -37,6 +37,8 @@ module.exports = {
], ],
"vue/singleline-html-element-content-newline": "off", "vue/singleline-html-element-content-newline": "off",
"vue/multiline-html-element-content-newline": "off", "vue/multiline-html-element-content-newline": "off",
"vue/require-default-prop": "warn",
"vue/require-prop-types": "warn",
"prettier/prettier": ["warn", { semi: false }], "prettier/prettier": ["warn", { semi: false }],
"import/no-named-as-default": "off", "import/no-named-as-default": "off",
"no-undef": "off", "no-undef": "off",

View File

@@ -12,15 +12,20 @@
</div> </div>
<div slot="body" class="flex flex-col"> <div slot="body" class="flex flex-col">
<label for="selectLabel">{{ $t("token_req_name") }}</label> <label for="selectLabel">{{ $t("token_req_name") }}</label>
<input type="text" id="selectLabel" v-model="requestData.name" @keyup.enter="saveRequestAs" /> <input
id="selectLabel"
v-model="requestData.name"
type="text"
@keyup.enter="saveRequestAs"
/>
<label for="selectLabel">Select location</label> <label for="selectLabel">Select location</label>
<!-- <input readonly :value="path" /> --> <!-- <input readonly :value="path" /> -->
<Collections <Collections
:picked="picked"
:save-request="true"
@select="onSelect" @select="onSelect"
@update-collection="collectionsType.type = $event" @update-collection="collectionsType.type = $event"
@update-coll-type="onUpdateCollType" @update-coll-type="onUpdateCollType"
:picked="picked"
:saveRequest="true"
/> />
</div> </div>
<div slot="footer"> <div slot="footer">
@@ -42,12 +47,12 @@
<script> <script>
import { fb } from "~/helpers/fb" import { fb } from "~/helpers/fb"
import { getSettingSubject } from "~/newstore/settings" import { getSettingSubject } from "~/newstore/settings"
import * as team_utils from "~/helpers/teams/utils" import * as teamUtils from "~/helpers/teams/utils"
export default { export default {
props: { props: {
show: Boolean, show: Boolean,
editingRequest: Object, editingRequest: { type: Object, default: () => {} },
}, },
data() { data() {
return { return {
@@ -71,20 +76,6 @@ export default {
SYNC_COLLECTIONS: getSettingSubject("syncCollections"), SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
} }
}, },
watch: {
"requestData.collectionIndex": function resetFolderAndRequestIndex() {
// if user has chosen some folder, than selected other collection, which doesn't have any folders
// than `requestUpdateData.folderName` won't be reseted
this.$data.requestData.folderName = undefined
this.$data.requestData.requestIndex = undefined
},
"requestData.folderName": function resetRequestIndex() {
this.$data.requestData.requestIndex = undefined
},
editingRequest({ name }) {
this.$data.requestData.name = name || this.$data.defaultRequestName
},
},
computed: { computed: {
folders() { folders() {
const collections = this.$store.state.postwoman.collections const collections = this.$store.state.postwoman.collections
@@ -107,7 +98,8 @@ export default {
return [] return []
} }
const userSelectedAnyFolder = folderName !== undefined && folderName !== "" const userSelectedAnyFolder =
folderName !== undefined && folderName !== ""
if (userSelectedAnyFolder) { if (userSelectedAnyFolder) {
const collection = collections[collectionIndex] const collection = collections[collectionIndex]
@@ -125,6 +117,20 @@ export default {
} }
}, },
}, },
watch: {
"requestData.collectionIndex": function resetFolderAndRequestIndex() {
// if user has chosen some folder, than selected other collection, which doesn't have any folders
// than `requestUpdateData.folderName` won't be reseted
this.$data.requestData.folderName = undefined
this.$data.requestData.requestIndex = undefined
},
"requestData.folderName": function resetRequestIndex() {
this.$data.requestData.requestIndex = undefined
},
editingRequest({ name }) {
this.$data.requestData.name = name || this.$data.defaultRequestName
},
},
methods: { methods: {
onUpdateCollType(newCollType) { onUpdateCollType(newCollType) {
this.collectionsType = newCollType this.collectionsType = newCollType
@@ -188,14 +194,14 @@ export default {
this.syncCollections() this.syncCollections()
} else if (this.picked.pickedType === "teams-request") { } else if (this.picked.pickedType === "teams-request") {
team_utils.overwriteRequestTeams( teamUtils.overwriteRequestTeams(
this.$apollo, this.$apollo,
JSON.stringify(requestUpdated), JSON.stringify(requestUpdated),
requestUpdated.name, requestUpdated.name,
this.picked.requestID this.picked.requestID
) )
} else if (this.picked.pickedType === "teams-folder") { } else if (this.picked.pickedType === "teams-folder") {
team_utils.saveRequestAsTeams( teamUtils.saveRequestAsTeams(
this.$apollo, this.$apollo,
JSON.stringify(requestUpdated), JSON.stringify(requestUpdated),
requestUpdated.name, requestUpdated.name,
@@ -203,7 +209,7 @@ export default {
this.picked.folderID this.picked.folderID
) )
} else if (this.picked.pickedType === "teams-collection") { } else if (this.picked.pickedType === "teams-collection") {
team_utils.saveRequestAsTeams( teamUtils.saveRequestAsTeams(
this.$apollo, this.$apollo,
JSON.stringify(requestUpdated), JSON.stringify(requestUpdated),
requestUpdated.name, requestUpdated.name,

View File

@@ -1,15 +1,23 @@
<template> <template>
<AppSection class="yellow" :label="$t('collections')" ref="collections" no-legend> <AppSection
ref="collections"
class="yellow"
:label="$t('collections')"
no-legend
>
<div class="show-on-large-screen"> <div class="show-on-large-screen">
<input <input
v-model="filterText"
aria-label="Search" aria-label="Search"
type="search" type="search"
:placeholder="$t('search')" :placeholder="$t('search')"
v-model="filterText"
class="rounded-t-lg" class="rounded-t-lg"
/> />
</div> </div>
<CollectionsGraphqlAdd :show="showModalAdd" @hide-modal="displayModalAdd(false)" /> <CollectionsGraphqlAdd
:show="showModalAdd"
@hide-modal="displayModalAdd(false)"
/>
<CollectionsGraphqlEdit <CollectionsGraphqlEdit
:show="showModalEdit" :show="showModalEdit"
:editing-collection="editingCollection" :editing-collection="editingCollection"
@@ -53,17 +61,21 @@
</button> </button>
</div> </div>
<p v-if="collections.length === 0" class="info"> <p v-if="collections.length === 0" class="info">
<i class="material-icons">help_outline</i> {{ $t("create_new_collection") }} <i class="material-icons">help_outline</i>
{{ $t("create_new_collection") }}
</p> </p>
<div class="virtual-list"> <div class="virtual-list">
<ul class="flex-col"> <ul class="flex-col">
<li v-for="(collection, index) in filteredCollections" :key="collection.name"> <li
v-for="(collection, index) in filteredCollections"
:key="collection.name"
>
<CollectionsGraphqlCollection <CollectionsGraphqlCollection
:name="collection.name" :name="collection.name"
:collection-index="index" :collection-index="index"
:collection="collection" :collection="collection"
:doc="doc" :doc="doc"
:isFiltered="filterText.length > 0" :is-filtered="filterText.length > 0"
@edit-collection="editCollection(collection, index)" @edit-collection="editCollection(collection, index)"
@add-folder="addFolder($event)" @add-folder="addFolder($event)"
@edit-folder="editFolder($event)" @edit-folder="editFolder($event)"
@@ -74,17 +86,13 @@
</ul> </ul>
</div> </div>
<p v-if="filterText && filteredCollections.length === 0" class="info"> <p v-if="filterText && filteredCollections.length === 0" class="info">
<i class="material-icons">not_interested</i> {{ $t("nothing_found") }} "{{ filterText }}" <i class="material-icons">not_interested</i> {{ $t("nothing_found") }} "{{
filterText
}}"
</p> </p>
</AppSection> </AppSection>
</template> </template>
<style scoped lang="scss">
.virtual-list {
max-height: calc(100vh - 270px);
}
</style>
<script> <script>
import { fb } from "~/helpers/fb" import { fb } from "~/helpers/fb"
@@ -128,15 +136,16 @@ export default {
const filterText = this.filterText.toLowerCase() const filterText = this.filterText.toLowerCase()
const filteredCollections = [] const filteredCollections = []
for (let collection of collections) { for (const collection of collections) {
const filteredRequests = [] const filteredRequests = []
const filteredFolders = [] const filteredFolders = []
for (let request of collection.requests) { for (const request of collection.requests) {
if (request.name.toLowerCase().includes(filterText)) filteredRequests.push(request) if (request.name.toLowerCase().includes(filterText))
filteredRequests.push(request)
} }
for (let folder of collection.folders) { for (const folder of collection.folders) {
const filteredFolderRequests = [] const filteredFolderRequests = []
for (let request of folder.requests) { for (const request of folder.requests) {
if (request.name.toLowerCase().includes(filterText)) if (request.name.toLowerCase().includes(filterText))
filteredFolderRequests.push(request) filteredFolderRequests.push(request)
} }
@@ -158,7 +167,7 @@ export default {
return filteredCollections return filteredCollections
}, },
}, },
async mounted() { mounted() {
this._keyListener = function (e) { this._keyListener = function (e) {
if (e.key === "Escape") { if (e.key === "Escape") {
e.preventDefault() e.preventDefault()
@@ -173,6 +182,9 @@ export default {
} }
document.addEventListener("keydown", this._keyListener.bind(this)) document.addEventListener("keydown", this._keyListener.bind(this))
}, },
beforeDestroy() {
document.removeEventListener("keydown", this._keyListener)
},
methods: { methods: {
displayModalAdd(shouldDisplay) { displayModalAdd(shouldDisplay) {
this.showModalAdd = shouldDisplay this.showModalAdd = shouldDisplay
@@ -232,7 +244,13 @@ export default {
this.syncCollections() this.syncCollections()
}, },
editRequest(payload) { editRequest(payload) {
const { collectionIndex, folderIndex, folderName, request, requestIndex } = payload const {
collectionIndex,
folderIndex,
folderName,
request,
requestIndex,
} = payload
this.$data.editingCollectionIndex = collectionIndex this.$data.editingCollectionIndex = collectionIndex
this.$data.editingFolderIndex = folderIndex this.$data.editingFolderIndex = folderIndex
this.$data.editingFolderName = folderName this.$data.editingFolderName = folderName
@@ -253,15 +271,20 @@ export default {
if (fb.currentUser !== null && fb.currentSettings[0]) { if (fb.currentUser !== null && fb.currentSettings[0]) {
if (fb.currentSettings[0].value) { if (fb.currentSettings[0].value) {
fb.writeCollections( fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)), JSON.parse(
JSON.stringify(this.$store.state.postwoman.collectionsGraphql)
),
"collectionsGraphql" "collectionsGraphql"
) )
} }
} }
}, },
}, },
beforeDestroy() {
document.removeEventListener("keydown", this._keyListener)
},
} }
</script> </script>
<style scoped lang="scss">
.virtual-list {
max-height: calc(100vh - 270px);
}
</style>

View File

@@ -1,17 +1,17 @@
<template> <template>
<AppSection :label="$t('collections')" ref="collections" no-legend> <AppSection ref="collections" :label="$t('collections')" no-legend>
<div class="show-on-large-screen"> <div class="show-on-large-screen">
<input <input
v-if="!saveRequest"
v-model="filterText"
aria-label="Search" aria-label="Search"
type="search" type="search"
:placeholder="$t('search')" :placeholder="$t('search')"
v-if="!saveRequest"
v-model="filterText"
class="rounded-t-lg" class="rounded-t-lg"
/> />
</div> </div>
<CollectionsChooseType <CollectionsChooseType
:collectionsType="collectionsType" :collections-type="collectionsType"
:show="showTeamCollections" :show="showTeamCollections"
:doc="doc" :doc="doc"
@update-collection-type="updateCollectionType" @update-collection-type="updateCollectionType"
@@ -48,7 +48,7 @@
/> />
<CollectionsImportExport <CollectionsImportExport
:show="showModalImportExport" :show="showModalImportExport"
:collectionsType="collectionsType" :collections-type="collectionsType"
@hide-modal="displayModalImportExport(false)" @hide-modal="displayModalImportExport(false)"
@update-team-collections="updateTeamCollections" @update-team-collections="updateTeamCollections"
/> />
@@ -61,22 +61,27 @@
!saveRequest !saveRequest
" "
class="icon" class="icon"
@click="displayModalAdd(true)"
disabled disabled
@click="displayModalAdd(true)"
> >
<i class="material-icons">add</i> <i class="material-icons">add</i>
<div v-tooltip.left="$t('disable_new_collection')"> <div v-tooltip.left="$t('disable_new_collection')">
<span>{{ $t("new") }}</span> <span>{{ $t("new") }}</span>
</div> </div>
</button> </button>
<button v-else-if="!saveRequest" class="icon" @click="displayModalAdd(true)"> <button
v-else-if="!saveRequest"
class="icon"
@click="displayModalAdd(true)"
>
<i class="material-icons">add</i> <i class="material-icons">add</i>
<span>{{ $t("new") }}</span> <span>{{ $t("new") }}</span>
</button> </button>
<button <button
v-if="!saveRequest" v-if="!saveRequest"
:disabled=" :disabled="
collectionsType.type == 'team-collections' && collectionsType.selectedTeam == undefined collectionsType.type == 'team-collections' &&
collectionsType.selectedTeam == undefined
" "
class="icon" class="icon"
@click="displayModalImportExport(true)" @click="displayModalImportExport(true)"
@@ -85,11 +90,15 @@
</button> </button>
</div> </div>
<p v-if="collections.length === 0" class="info"> <p v-if="collections.length === 0" class="info">
<i class="material-icons">help_outline</i> {{ $t("create_new_collection") }} <i class="material-icons">help_outline</i>
{{ $t("create_new_collection") }}
</p> </p>
<div class="virtual-list"> <div class="virtual-list">
<ul class="flex-col"> <ul class="flex-col">
<li v-for="(collection, index) in filteredCollections" :key="collection.name"> <li
v-for="(collection, index) in filteredCollections"
:key="collection.name"
>
<component <component
:is=" :is="
collectionsType.type == 'my-collections' collectionsType.type == 'my-collections'
@@ -100,10 +109,10 @@
:collection-index="index" :collection-index="index"
:collection="collection" :collection="collection"
:doc="doc" :doc="doc"
:isFiltered="filterText.length > 0" :is-filtered="filterText.length > 0"
:selected="selected.some((coll) => coll == collection)" :selected="selected.some((coll) => coll == collection)"
:saveRequest="saveRequest" :save-request="saveRequest"
:collectionsType="collectionsType" :collections-type="collectionsType"
:picked="picked" :picked="picked"
@edit-collection="editCollection(collection, index)" @edit-collection="editCollection(collection, index)"
@add-folder="addFolder($event)" @add-folder="addFolder($event)"
@@ -112,25 +121,6 @@
@update-team-collections="updateTeamCollections" @update-team-collections="updateTeamCollections"
@select-collection="$emit('use-collection', collection)" @select-collection="$emit('use-collection', collection)"
@unselect-collection="$emit('remove-collection', collection)" @unselect-collection="$emit('remove-collection', collection)"
@select-folder="
$emit('select-folder', {
folderName:
(collectionsType.type == 'my-collections' ? collection.name : collection.title) +
'/' +
$event.name,
collectionIndex: collectionsType.type == 'my-collections' ? index : $event.id,
reqIdx: $event.reqIdx,
collectionsType: collectionsType,
folderId: $event.id,
})
if (collectionsType.type == 'my-collections') {
if ($event.folderPath) {
picked = $event.folderPath
} else picked = index
} else {
picked = $event.id
}
"
@select="$emit('select', $event)" @select="$emit('select', $event)"
@expand-collection="expandCollection" @expand-collection="expandCollection"
@remove-collection="removeCollection" @remove-collection="removeCollection"
@@ -140,31 +130,27 @@
</ul> </ul>
</div> </div>
<p v-if="filterText && filteredCollections.length === 0" class="info"> <p v-if="filterText && filteredCollections.length === 0" class="info">
<i class="material-icons">not_interested</i> {{ $t("nothing_found") }} "{{ filterText }}" <i class="material-icons">not_interested</i> {{ $t("nothing_found") }} "{{
filterText
}}"
</p> </p>
</AppSection> </AppSection>
</template> </template>
<style scoped lang="scss">
.virtual-list {
max-height: calc(100vh - 270px);
}
</style>
<script> <script>
import gql from "graphql-tag"
import cloneDeep from "lodash/cloneDeep"
import { fb } from "~/helpers/fb" import { fb } from "~/helpers/fb"
import { getSettingSubject } from "~/newstore/settings" import { getSettingSubject } from "~/newstore/settings"
import gql from "graphql-tag"
import TeamCollectionAdapter from "~/helpers/teams/TeamCollectionAdapter" import TeamCollectionAdapter from "~/helpers/teams/TeamCollectionAdapter"
import * as team_utils from "~/helpers/teams/utils" import * as teamUtils from "~/helpers/teams/utils"
import cloneDeep from "lodash/cloneDeep"
export default { export default {
props: { props: {
doc: Boolean, doc: Boolean,
selected: { type: Array, default: () => [] }, selected: { type: Array, default: () => [] },
saveRequest: Boolean, saveRequest: Boolean,
picked: Object, picked: { type: Object, default: () => {} },
}, },
data() { data() {
return { return {
@@ -196,20 +182,12 @@ export default {
SYNC_COLLECTIONS: getSettingSubject("syncCollections"), SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
} }
}, },
watch: {
"collectionsType.type": function emitstuff() {
this.$emit("update-collection", this.$data.collectionsType.type)
},
"collectionsType.selectedTeam": function (value) {
if (value?.id) this.teamCollectionAdapter.changeTeamID(value.id)
},
},
computed: { computed: {
showTeamCollections() { showTeamCollections() {
if (fb.currentUser == null) { if (fb.currentUser == null) {
this.collectionsType.type = "my-collections" return false
} }
return fb.currentUser !== null return true
}, },
collections() { collections() {
return fb.currentUser !== null return fb.currentUser !== null
@@ -218,9 +196,11 @@ export default {
}, },
filteredCollections() { filteredCollections() {
let collections = null let collections = null
if (this.collectionsType.type == "my-collections") { if (this.collectionsType.type === "my-collections") {
collections = collections =
fb.currentUser !== null ? fb.currentCollections : this.$store.state.postwoman.collections fb.currentUser !== null
? fb.currentCollections
: this.$store.state.postwoman.collections
} else { } else {
collections = this.teamCollectionsNew collections = this.teamCollectionsNew
} }
@@ -229,24 +209,25 @@ export default {
return collections return collections
} }
if (this.collectionsType.type == "team-collections") { if (this.collectionsType.type === "team-collections") {
return [] return []
} }
const filterText = this.filterText.toLowerCase() const filterText = this.filterText.toLowerCase()
const filteredCollections = [] const filteredCollections = []
for (let collection of collections) { for (const collection of collections) {
const filteredRequests = [] const filteredRequests = []
const filteredFolders = [] const filteredFolders = []
for (let request of collection.requests) { for (const request of collection.requests) {
if (request.name.toLowerCase().includes(filterText)) filteredRequests.push(request) if (request.name.toLowerCase().includes(filterText))
filteredRequests.push(request)
} }
for (let folder of this.collectionsType.type === "team-collections" for (const folder of this.collectionsType.type === "team-collections"
? collection.children ? collection.children
: collection.folders) { : collection.folders) {
const filteredFolderRequests = [] const filteredFolderRequests = []
for (let request of folder.requests) { for (const request of folder.requests) {
if (request.name.toLowerCase().includes(filterText)) if (request.name.toLowerCase().includes(filterText))
filteredFolderRequests.push(request) filteredFolderRequests.push(request)
} }
@@ -271,7 +252,15 @@ export default {
return filteredCollections return filteredCollections
}, },
}, },
async mounted() { watch: {
"collectionsType.type": function emitstuff() {
this.$emit("update-collection", this.$data.collectionsType.type)
},
"collectionsType.selectedTeam"(value) {
if (value?.id) this.teamCollectionAdapter.changeTeamID(value.id)
},
},
mounted() {
this._keyListener = function (e) { this._keyListener = function (e) {
if (e.key === "Escape") { if (e.key === "Escape") {
e.preventDefault() e.preventDefault()
@@ -290,6 +279,9 @@ export default {
this.teamCollectionsNew = cloneDeep(colls) this.teamCollectionsNew = cloneDeep(colls)
}) })
}, },
beforeDestroy() {
document.removeEventListener("keydown", this._keyListener)
},
methods: { methods: {
updateTeamCollections() { updateTeamCollections() {
// TODO: Remove this at some point // TODO: Remove this at some point
@@ -319,8 +311,12 @@ export default {
this.collectionsType.type === "team-collections" && this.collectionsType.type === "team-collections" &&
this.collectionsType.selectedTeam.myRole !== "VIEWER" this.collectionsType.selectedTeam.myRole !== "VIEWER"
) { ) {
team_utils teamUtils
.createNewRootCollection(this.$apollo, name, this.collectionsType.selectedTeam.id) .createNewRootCollection(
this.$apollo,
name,
this.collectionsType.selectedTeam.id
)
.then(() => { .then(() => {
this.$toast.success(this.$t("collection_added"), { this.$toast.success(this.$t("collection_added"), {
icon: "done", icon: "done",
@@ -356,7 +352,7 @@ export default {
this.collectionsType.type === "team-collections" && this.collectionsType.type === "team-collections" &&
this.collectionsType.selectedTeam.myRole !== "VIEWER" this.collectionsType.selectedTeam.myRole !== "VIEWER"
) { ) {
team_utils teamUtils
.renameCollection(this.$apollo, newName, this.editingCollection.id) .renameCollection(this.$apollo, newName, this.editingCollection.id)
.then(() => { .then(() => {
// TODO: $t translations ? // TODO: $t translations ?
@@ -375,20 +371,20 @@ export default {
}, },
// Intended to be called by CollectionEditFolder modal submit event // Intended to be called by CollectionEditFolder modal submit event
updateEditingFolder(name) { updateEditingFolder(name) {
if (this.collectionsType.type == "my-collections") { if (this.collectionsType.type === "my-collections") {
this.$store.commit("postwoman/editFolder", { this.$store.commit("postwoman/editFolder", {
collectionIndex: this.editingCollectionIndex, collectionIndex: this.editingCollectionIndex,
folder: { ...this.editingFolder, name: name }, folder: { ...this.editingFolder, name },
folderIndex: this.editingFolderIndex, folderIndex: this.editingFolderIndex,
folderName: this.editingFolder.name, folderName: this.editingFolder.name,
flag: "rest", flag: "rest",
}) })
this.syncCollections() this.syncCollections()
} else if ( } else if (
this.collectionsType.type == "team-collections" && this.collectionsType.type === "team-collections" &&
this.collectionsType.selectedTeam.myRole !== "VIEWER" this.collectionsType.selectedTeam.myRole !== "VIEWER"
) { ) {
team_utils teamUtils
.renameCollection(this.$apollo, name, this.editingFolder.id) .renameCollection(this.$apollo, name, this.editingFolder.id)
.then(() => { .then(() => {
// Result // Result
@@ -414,7 +410,7 @@ export default {
name: requestUpdateData.name || this.editingRequest.name, name: requestUpdateData.name || this.editingRequest.name,
} }
if (this.collectionsType.type == "my-collections") { if (this.collectionsType.type === "my-collections") {
this.$store.commit("postwoman/editRequest", { this.$store.commit("postwoman/editRequest", {
requestCollectionIndex: this.editingCollectionIndex, requestCollectionIndex: this.editingCollectionIndex,
requestFolderName: this.editingFolderName, requestFolderName: this.editingFolderName,
@@ -428,9 +424,14 @@ export default {
this.collectionsType.type === "team-collections" && this.collectionsType.type === "team-collections" &&
this.collectionsType.selectedTeam.myRole !== "VIEWER" this.collectionsType.selectedTeam.myRole !== "VIEWER"
) { ) {
let requestName = requestUpdateData.name || this.editingRequest.name const requestName = requestUpdateData.name || this.editingRequest.name
team_utils teamUtils
.updateRequest(this.$apollo, requestUpdated, requestName, this.editingRequestIndex) .updateRequest(
this.$apollo,
requestUpdated,
requestName,
this.editingRequestIndex
)
.then(() => { .then(() => {
this.$toast.success("Request Renamed", { this.$toast.success("Request Renamed", {
icon: "done", icon: "done",
@@ -489,12 +490,18 @@ export default {
}) })
this.syncCollections() this.syncCollections()
} else if (this.collectionsType.type === "team-collections") { } else if (this.collectionsType.type === "team-collections") {
if (this.collectionsType.selectedTeam.myRole != "VIEWER") { if (this.collectionsType.selectedTeam.myRole !== "VIEWER") {
this.$apollo this.$apollo
.mutate({ .mutate({
mutation: gql` mutation: gql`
mutation CreateChildCollection($childTitle: String!, $collectionID: String!) { mutation CreateChildCollection(
createChildCollection(childTitle: $childTitle, collectionID: $collectionID) { $childTitle: String!
$collectionID: String!
) {
createChildCollection(
childTitle: $childTitle
collectionID: $collectionID
) {
id id
} }
} }
@@ -540,7 +547,13 @@ export default {
this.syncCollections() this.syncCollections()
}, },
editRequest(payload) { editRequest(payload) {
const { collectionIndex, folderIndex, folderName, request, requestIndex } = payload const {
collectionIndex,
folderIndex,
folderName,
request,
requestIndex,
} = payload
this.$data.editingCollectionIndex = collectionIndex this.$data.editingCollectionIndex = collectionIndex
this.$data.editingFolderIndex = folderIndex this.$data.editingFolderIndex = folderIndex
this.$data.editingFolderName = folderName this.$data.editingFolderName = folderName
@@ -570,17 +583,17 @@ export default {
this.teamCollectionAdapter.expandCollection(collectionID) this.teamCollectionAdapter.expandCollection(collectionID)
}, },
removeCollection({ collectionsType, collectionIndex, collectionID }) { removeCollection({ collectionsType, collectionIndex, collectionID }) {
if (collectionsType.type == "my-collections") { if (collectionsType.type === "my-collections") {
this.$store.commit("postwoman/removeCollection", { this.$store.commit("postwoman/removeCollection", {
collectionIndex: collectionIndex, collectionIndex,
flag: "rest", flag: "rest",
}) })
this.$toast.error(this.$t("deleted"), { this.$toast.error(this.$t("deleted"), {
icon: "delete", icon: "delete",
}) })
this.syncCollections() this.syncCollections()
} else if (collectionsType.type == "team-collections") { } else if (collectionsType.type === "team-collections") {
if (collectionsType.selectedTeam.myRole != "VIEWER") { if (collectionsType.selectedTeam.myRole !== "VIEWER") {
this.$apollo this.$apollo
.mutate({ .mutate({
// Query // Query
@@ -591,10 +604,10 @@ export default {
`, `,
// Parameters // Parameters
variables: { variables: {
collectionID: collectionID, collectionID,
}, },
}) })
.then((data) => { .then(() => {
// Result // Result
this.$toast.success(this.$t("deleted"), { this.$toast.success(this.$t("deleted"), {
icon: "delete", icon: "delete",
@@ -611,21 +624,21 @@ export default {
} }
}, },
removeRequest({ collectionIndex, folderName, requestIndex }) { removeRequest({ collectionIndex, folderName, requestIndex }) {
if (this.collectionsType.type == "my-collections") { if (this.collectionsType.type === "my-collections") {
this.$store.commit("postwoman/removeRequest", { this.$store.commit("postwoman/removeRequest", {
collectionIndex: collectionIndex, collectionIndex,
folderName: folderName, folderName,
requestIndex: requestIndex, requestIndex,
flag: "rest", flag: "rest",
}) })
this.$toast.error(this.$t("deleted"), { this.$toast.error(this.$t("deleted"), {
icon: "delete", icon: "delete",
}) })
this.syncCollections() this.syncCollections()
} else if (this.collectionsType.type == "team-collections") { } else if (this.collectionsType.type === "team-collections") {
team_utils teamUtils
.deleteRequest(this.$apollo, requestIndex) .deleteRequest(this.$apollo, requestIndex)
.then((data) => { .then(() => {
// Result // Result
this.$toast.success(this.$t("deleted"), { this.$toast.success(this.$t("deleted"), {
icon: "delete", icon: "delete",
@@ -641,8 +654,11 @@ export default {
} }
}, },
}, },
beforeDestroy() {
document.removeEventListener("keydown", this._keyListener)
},
} }
</script> </script>
<style scoped lang="scss">
.virtual-list {
max-height: calc(100vh - 270px);
}
</style>

View File

@@ -1,7 +1,10 @@
<template> <template>
<div> <div>
<div <div
:class="['row-wrapper transition duration-150 ease-in-out', { 'bg-bgDarkColor': dragging }]" :class="[
'row-wrapper transition duration-150 ease-in-out',
{ 'bg-bgDarkColor': dragging },
]"
@dragover.prevent @dragover.prevent
@drop.prevent="dropEvent" @drop.prevent="dropEvent"
@dragover="dragging = true" @dragover="dragging = true"
@@ -10,10 +13,16 @@
@dragend="dragging = false" @dragend="dragging = false"
> >
<button class="icon" @click="toggleShowChildren"> <button class="icon" @click="toggleShowChildren">
<i class="material-icons" v-show="!showChildren && !isFiltered">arrow_right</i> <i v-show="!showChildren && !isFiltered" class="material-icons"
<i class="material-icons" v-show="showChildren || isFiltered">arrow_drop_down</i> >arrow_right</i
>
<i v-show="showChildren || isFiltered" class="material-icons"
>arrow_drop_down</i
>
<i v-if="isSelected" class="text-green-400 material-icons">check_circle</i> <i v-if="isSelected" class="text-green-400 material-icons"
>check_circle</i
>
<i v-else class="material-icons">folder</i> <i v-else class="material-icons">folder</i>
<span>{{ collection.name }}</span> <span>{{ collection.name }}</span>
@@ -21,43 +30,56 @@
<div> <div>
<button <button
v-if="doc && !selected" v-if="doc && !selected"
v-tooltip.left="$t('import')"
class="icon" class="icon"
@click="$emit('select-collection')" @click="$emit('select-collection')"
v-tooltip.left="$t('import')"
> >
<i class="material-icons">check_box_outline_blank</i> <i class="material-icons">check_box_outline_blank</i>
</button> </button>
<button <button
v-if="doc && selected" v-if="doc && selected"
v-tooltip.left="$t('delete')"
class="icon" class="icon"
@click="$emit('unselect-collection')" @click="$emit('unselect-collection')"
v-tooltip.left="$t('delete')"
> >
<i class="material-icons">check_box</i> <i class="material-icons">check_box</i>
</button> </button>
<v-popover v-if="!saveRequest"> <v-popover v-if="!saveRequest">
<button class="tooltip-target icon" v-tooltip.left="$t('more')"> <button v-tooltip.left="$t('more')" class="tooltip-target icon">
<i class="material-icons">more_vert</i> <i class="material-icons">more_vert</i>
</button> </button>
<template slot="popover"> <template slot="popover">
<div> <div>
<button <button
class="icon"
@click="$emit('add-folder', { folder: collection, path: `${collectionIndex}` })"
v-close-popover v-close-popover
class="icon"
@click="
$emit('add-folder', {
folder: collection,
path: `${collectionIndex}`,
})
"
> >
<i class="material-icons">create_new_folder</i> <i class="material-icons">create_new_folder</i>
<span>{{ $t("new_folder") }}</span> <span>{{ $t("new_folder") }}</span>
</button> </button>
</div> </div>
<div> <div>
<button class="icon" @click="$emit('edit-collection')" v-close-popover> <button
v-close-popover
class="icon"
@click="$emit('edit-collection')"
>
<i class="material-icons">create</i> <i class="material-icons">create</i>
<span>{{ $t("edit") }}</span> <span>{{ $t("edit") }}</span>
</button> </button>
</div> </div>
<div> <div>
<button class="icon" @click="confirmRemove = true" v-close-popover> <button
v-close-popover
class="icon"
@click="confirmRemove = true"
>
<i class="material-icons">delete</i> <i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span> <span>{{ $t("delete") }}</span>
</button> </button>
@@ -79,9 +101,9 @@
:folder-path="`${collectionIndex}/${index}`" :folder-path="`${collectionIndex}/${index}`"
:collection-index="collectionIndex" :collection-index="collectionIndex"
:doc="doc" :doc="doc"
:saveRequest="saveRequest" :save-request="saveRequest"
:collectionsType="collectionsType" :collections-type="collectionsType"
:isFiltered="isFiltered" :is-filtered="isFiltered"
:picked="picked" :picked="picked"
@add-folder="$emit('add-folder', $event)" @add-folder="$emit('add-folder', $event)"
@edit-folder="$emit('edit-folder', $event)" @edit-folder="$emit('edit-folder', $event)"
@@ -105,8 +127,8 @@
:folder-path="collectionIndex.toString()" :folder-path="collectionIndex.toString()"
:request-index="index" :request-index="index"
:doc="doc" :doc="doc"
:saveRequest="saveRequest" :save-request="saveRequest"
:collectionsType="collectionsType" :collections-type="collectionsType"
:picked="picked" :picked="picked"
@edit-request="editRequest($event)" @edit-request="editRequest($event)"
@select="$emit('select', $event)" @select="$emit('select', $event)"
@@ -117,13 +139,16 @@
<ul> <ul>
<li <li
v-if=" v-if="
(collection.folders == undefined || collection.folders.length === 0) && (collection.folders == undefined ||
(collection.requests == undefined || collection.requests.length === 0) collection.folders.length === 0) &&
(collection.requests == undefined ||
collection.requests.length === 0)
" "
class="flex ml-8 border-l border-brdColor" class="flex ml-8 border-l border-brdColor"
> >
<p class="info"> <p class="info">
<i class="material-icons">not_interested</i> {{ $t("collection_empty") }} <i class="material-icons">not_interested</i>
{{ $t("collection_empty") }}
</p> </p>
</li> </li>
</ul> </ul>
@@ -143,14 +168,14 @@ import { getSettingSubject } from "~/newstore/settings"
export default { export default {
props: { props: {
collectionIndex: Number, collectionIndex: { type: Number, default: null },
collection: Object, collection: { type: Object, default: () => {} },
doc: Boolean, doc: Boolean,
isFiltered: Boolean, isFiltered: Boolean,
selected: Boolean, selected: Boolean,
saveRequest: Boolean, saveRequest: Boolean,
collectionsType: Object, collectionsType: { type: Object, default: () => {} },
picked: Object, picked: { type: Object, default: () => {} },
}, },
data() { data() {
return { return {

View File

@@ -1,7 +1,10 @@
<template> <template>
<div> <div>
<div <div
:class="['row-wrapper transition duration-150 ease-in-out', { 'bg-bgDarkColor': dragging }]" :class="[
'row-wrapper transition duration-150 ease-in-out',
{ 'bg-bgDarkColor': dragging },
]"
@dragover.prevent @dragover.prevent
@drop.prevent="dropEvent" @drop.prevent="dropEvent"
@dragover="dragging = true" @dragover="dragging = true"
@@ -11,23 +14,29 @@
> >
<div> <div>
<button class="icon" @click="toggleShowChildren"> <button class="icon" @click="toggleShowChildren">
<i class="material-icons" v-show="!showChildren && !isFiltered">arrow_right</i> <i v-show="!showChildren && !isFiltered" class="material-icons"
<i class="material-icons" v-show="showChildren || isFiltered">arrow_drop_down</i> >arrow_right</i
<i v-if="isSelected" class="text-green-400 material-icons">check_circle</i> >
<i v-show="showChildren || isFiltered" class="material-icons"
>arrow_drop_down</i
>
<i v-if="isSelected" class="text-green-400 material-icons"
>check_circle</i
>
<i v-else class="material-icons">folder_open</i> <i v-else class="material-icons">folder_open</i>
<span>{{ folder.name ? folder.name : folder.title }}</span> <span>{{ folder.name ? folder.name : folder.title }}</span>
</button> </button>
</div> </div>
<v-popover v-if="!saveRequest"> <v-popover v-if="!saveRequest">
<button class="tooltip-target icon" v-tooltip.left="$t('more')"> <button v-tooltip.left="$t('more')" class="tooltip-target icon">
<i class="material-icons">more_vert</i> <i class="material-icons">more_vert</i>
</button> </button>
<template slot="popover"> <template slot="popover">
<div> <div>
<button <button
v-close-popover
class="icon" class="icon"
@click="$emit('add-folder', { folder, path: folderPath })" @click="$emit('add-folder', { folder, path: folderPath })"
v-close-popover
> >
<i class="material-icons">create_new_folder</i> <i class="material-icons">create_new_folder</i>
<span>{{ $t("new_folder") }}</span> <span>{{ $t("new_folder") }}</span>
@@ -35,16 +44,18 @@
</div> </div>
<div> <div>
<button <button
class="icon"
@click="$emit('edit-folder', { folder, folderIndex, collectionIndex })"
v-close-popover v-close-popover
class="icon"
@click="
$emit('edit-folder', { folder, folderIndex, collectionIndex })
"
> >
<i class="material-icons">edit</i> <i class="material-icons">edit</i>
<span>{{ $t("edit") }}</span> <span>{{ $t("edit") }}</span>
</button> </button>
</div> </div>
<div> <div>
<button class="icon" @click="confirmRemove = true" v-close-popover> <button v-close-popover class="icon" @click="confirmRemove = true">
<i class="material-icons">delete</i> <i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span> <span>{{ $t("delete") }}</span>
</button> </button>
@@ -64,8 +75,8 @@
:folder-index="subFolderIndex" :folder-index="subFolderIndex"
:collection-index="collectionIndex" :collection-index="collectionIndex"
:doc="doc" :doc="doc"
:saveRequest="saveRequest" :save-request="saveRequest"
:collectionsType="collectionsType" :collections-type="collectionsType"
:folder-path="`${folderPath}/${subFolderIndex}`" :folder-path="`${folderPath}/${subFolderIndex}`"
:picked="picked" :picked="picked"
@add-folder="$emit('add-folder', $event)" @add-folder="$emit('add-folder', $event)"
@@ -92,8 +103,8 @@
:folder-path="folderPath" :folder-path="folderPath"
:doc="doc" :doc="doc"
:picked="picked" :picked="picked"
:saveRequest="saveRequest" :save-request="saveRequest"
:collectionsType="collectionsType" :collections-type="collectionsType"
@edit-request="$emit('edit-request', $event)" @edit-request="$emit('edit-request', $event)"
@select="$emit('select', $event)" @select="$emit('select', $event)"
@remove-request="removeRequest" @remove-request="removeRequest"
@@ -109,7 +120,10 @@
" "
> >
<li class="flex ml-8 border-l border-brdColor"> <li class="flex ml-8 border-l border-brdColor">
<p class="info"><i class="material-icons">not_interested</i> {{ $t("folder_empty") }}</p> <p class="info">
<i class="material-icons">not_interested</i>
{{ $t("folder_empty") }}
</p>
</li> </li>
</ul> </ul>
</div> </div>
@@ -127,17 +141,17 @@ import { fb } from "~/helpers/fb"
import { getSettingSubject } from "~/newstore/settings" import { getSettingSubject } from "~/newstore/settings"
export default { export default {
name: "folder", name: "Folder",
props: { props: {
folder: Object, folder: { type: Object, default: () => {} },
folderIndex: Number, folderIndex: { type: Number, default: null },
collectionIndex: Number, collectionIndex: { type: Number, default: null },
folderPath: String, folderPath: { type: String, default: null },
doc: Boolean, doc: Boolean,
saveRequest: Boolean, saveRequest: Boolean,
isFiltered: Boolean, isFiltered: Boolean,
collectionsType: Object, collectionsType: { type: Object, default: () => {} },
picked: Object, picked: { type: Object, default: () => {} },
}, },
data() { data() {
return { return {

View File

@@ -1,7 +1,10 @@
<template> <template>
<div> <div>
<div <div
:class="['row-wrapper transition duration-150 ease-in-out', { 'bg-bgDarkColor': dragging }]" :class="[
'row-wrapper transition duration-150 ease-in-out',
{ 'bg-bgDarkColor': dragging },
]"
draggable="true" draggable="true"
@dragstart="dragStart" @dragstart="dragStart"
@dragover.stop @dragover.stop
@@ -10,23 +13,28 @@
> >
<div> <div>
<button <button
v-tooltip="!doc ? $t('use_request') : ''"
class="icon" class="icon"
@click="!doc ? selectRequest() : {}" @click="!doc ? selectRequest() : {}"
v-tooltip="!doc ? $t('use_request') : ''"
> >
<i v-if="isSelected" class="mx-3 text-green-400 material-icons">check_circle</i> <i v-if="isSelected" class="mx-3 text-green-400 material-icons"
>check_circle</i
>
<span v-else :class="getRequestLabelColor(request.method)">{{ request.method }}</span> <span v-else :class="getRequestLabelColor(request.method)">{{
request.method
}}</span>
<span>{{ request.name }}</span> <span>{{ request.name }}</span>
</button> </button>
</div> </div>
<v-popover v-if="!saveRequest"> <v-popover v-if="!saveRequest">
<button class="tooltip-target icon" v-tooltip="$t('more')"> <button v-tooltip="$t('more')" class="tooltip-target icon">
<i class="material-icons">more_vert</i> <i class="material-icons">more_vert</i>
</button> </button>
<template slot="popover"> <template slot="popover">
<div> <div>
<button <button
v-close-popover
class="icon" class="icon"
@click=" @click="
$emit('edit-request', { $emit('edit-request', {
@@ -37,14 +45,13 @@
requestIndex, requestIndex,
}) })
" "
v-close-popover
> >
<i class="material-icons">edit</i> <i class="material-icons">edit</i>
<span>{{ $t("edit") }}</span> <span>{{ $t("edit") }}</span>
</button> </button>
</div> </div>
<div> <div>
<button class="icon" @click="confirmRemove = true" v-close-popover> <button v-close-popover class="icon" @click="confirmRemove = true">
<i class="material-icons">delete</i> <i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span> <span>{{ $t("delete") }}</span>
</button> </button>
@@ -64,26 +71,17 @@
<script> <script>
export default { export default {
props: { props: {
request: Object, request: { type: Object, default: () => {} },
collectionIndex: Number, collectionIndex: { type: Number, default: null },
folderIndex: Number, folderIndex: { type: Number, default: null },
folderName: String, folderName: { type: String, default: null },
// eslint-disable-next-line vue/require-default-prop
requestIndex: [Number, String], requestIndex: [Number, String],
doc: Boolean, doc: Boolean,
saveRequest: Boolean, saveRequest: Boolean,
collectionsType: Object, collectionsType: { type: Object, default: () => {} },
folderPath: String, folderPath: { type: String, default: null },
picked: Object, picked: { type: Object, default: () => {} },
},
computed: {
isSelected() {
return (
this.picked &&
this.picked.pickedType === "my-request" &&
this.picked.folderPath === this.folderPath &&
this.picked.requestIndex === this.requestIndex
)
},
}, },
data() { data() {
return { return {
@@ -98,6 +96,16 @@ export default {
confirmRemove: false, confirmRemove: false,
} }
}, },
computed: {
isSelected() {
return (
this.picked &&
this.picked.pickedType === "my-request" &&
this.picked.folderPath === this.folderPath &&
this.picked.requestIndex === this.requestIndex
)
},
},
methods: { methods: {
selectRequest() { selectRequest() {
if (this.$props.saveRequest) if (this.$props.saveRequest)
@@ -110,7 +118,8 @@ export default {
requestIndex: this.requestIndex, requestIndex: this.requestIndex,
}, },
}) })
else this.$store.commit("postwoman/selectRequest", { request: this.request }) else
this.$store.commit("postwoman/selectRequest", { request: this.request })
}, },
dragStart({ dataTransfer }) { dragStart({ dataTransfer }) {
this.dragging = !this.dragging this.dragging = !this.dragging
@@ -127,7 +136,10 @@ export default {
}) })
}, },
getRequestLabelColor(method) { getRequestLabelColor(method) {
return this.requestMethodLabels[method.toLowerCase()] || this.requestMethodLabels.default return (
this.requestMethodLabels[method.toLowerCase()] ||
this.requestMethodLabels.default
)
}, },
}, },
} }

View File

@@ -2,10 +2,16 @@
<div> <div>
<div class="transition duration-150 ease-in-out row-wrapper"> <div class="transition duration-150 ease-in-out row-wrapper">
<button class="icon" @click="toggleShowChildren"> <button class="icon" @click="toggleShowChildren">
<i class="material-icons" v-show="!showChildren && !isFiltered">arrow_right</i> <i v-show="!showChildren && !isFiltered" class="material-icons"
<i class="material-icons" v-show="showChildren || isFiltered">arrow_drop_down</i> >arrow_right</i
>
<i v-show="showChildren || isFiltered" class="material-icons"
>arrow_drop_down</i
>
<i v-if="isSelected" class="text-green-400 material-icons">check_circle</i> <i v-if="isSelected" class="text-green-400 material-icons"
>check_circle</i
>
<i v-else class="material-icons">folder</i> <i v-else class="material-icons">folder</i>
<span>{{ collection.title }}</span> <span>{{ collection.title }}</span>
@@ -13,25 +19,25 @@
<div> <div>
<button <button
v-if="doc && !selected" v-if="doc && !selected"
v-tooltip.left="$t('import')"
class="icon" class="icon"
@click="$emit('select-collection')" @click="$emit('select-collection')"
v-tooltip.left="$t('import')"
> >
<i class="material-icons">check_box_outline_blank</i> <i class="material-icons">check_box_outline_blank</i>
</button> </button>
<button <button
v-if="doc && selected" v-if="doc && selected"
v-tooltip.left="$t('delete')"
class="icon" class="icon"
@click="$emit('unselect-collection')" @click="$emit('unselect-collection')"
v-tooltip.left="$t('delete')"
> >
<i class="material-icons">check_box</i> <i class="material-icons">check_box</i>
</button> </button>
<v-popover v-if="!saveRequest"> <v-popover v-if="!saveRequest">
<button <button
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'" v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
class="tooltip-target icon"
v-tooltip.left="$t('more')" v-tooltip.left="$t('more')"
class="tooltip-target icon"
> >
<i class="material-icons">more_vert</i> <i class="material-icons">more_vert</i>
</button> </button>
@@ -39,9 +45,14 @@
<div> <div>
<button <button
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'" v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
class="icon"
@click="$emit('add-folder', { folder: collection, path: `${collectionIndex}` })"
v-close-popover v-close-popover
class="icon"
@click="
$emit('add-folder', {
folder: collection,
path: `${collectionIndex}`,
})
"
> >
<i class="material-icons">create_new_folder</i> <i class="material-icons">create_new_folder</i>
<span>{{ $t("new_folder") }}</span> <span>{{ $t("new_folder") }}</span>
@@ -50,9 +61,9 @@
<div> <div>
<button <button
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'" v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
v-close-popover
class="icon" class="icon"
@click="$emit('edit-collection')" @click="$emit('edit-collection')"
v-close-popover
> >
<i class="material-icons">create</i> <i class="material-icons">create</i>
<span>{{ $t("edit") }}</span> <span>{{ $t("edit") }}</span>
@@ -61,9 +72,9 @@
<div> <div>
<button <button
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'" v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
v-close-popover
class="icon" class="icon"
@click="confirmRemove = true" @click="confirmRemove = true"
v-close-popover
> >
<i class="material-icons">delete</i> <i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span> <span>{{ $t("delete") }}</span>
@@ -86,9 +97,9 @@
:folder-path="`${collectionIndex}/${index}`" :folder-path="`${collectionIndex}/${index}`"
:collection-index="collectionIndex" :collection-index="collectionIndex"
:doc="doc" :doc="doc"
:saveRequest="saveRequest" :save-request="saveRequest"
:collectionsType="collectionsType" :collections-type="collectionsType"
:isFiltered="isFiltered" :is-filtered="isFiltered"
:picked="picked" :picked="picked"
@add-folder="$emit('add-folder', $event)" @add-folder="$emit('add-folder', $event)"
@edit-folder="$emit('edit-folder', $event)" @edit-folder="$emit('edit-folder', $event)"
@@ -112,8 +123,8 @@
:folder-name="collection.name" :folder-name="collection.name"
:request-index="request.id" :request-index="request.id"
:doc="doc" :doc="doc"
:saveRequest="saveRequest" :save-request="saveRequest"
:collectionsType="collectionsType" :collections-type="collectionsType"
:picked="picked" :picked="picked"
@edit-request="editRequest($event)" @edit-request="editRequest($event)"
@select="$emit('select', $event)" @select="$emit('select', $event)"
@@ -124,13 +135,16 @@
<ul> <ul>
<li <li
v-if=" v-if="
(collection.children == undefined || collection.children.length === 0) && (collection.children == undefined ||
(collection.requests == undefined || collection.requests.length === 0) collection.children.length === 0) &&
(collection.requests == undefined ||
collection.requests.length === 0)
" "
class="flex ml-8 border-l border-brdColor" class="flex ml-8 border-l border-brdColor"
> >
<p class="info"> <p class="info">
<i class="material-icons">not_interested</i> {{ $t("collection_empty") }} <i class="material-icons">not_interested</i>
{{ $t("collection_empty") }}
</p> </p>
</li> </li>
</ul> </ul>
@@ -147,14 +161,14 @@
<script> <script>
export default { export default {
props: { props: {
collectionIndex: Number, collectionIndex: { type: Number, default: null },
collection: Object, collection: { type: Object, default: () => {} },
doc: Boolean, doc: Boolean,
isFiltered: Boolean, isFiltered: Boolean,
selected: Boolean, selected: Boolean,
saveRequest: Boolean, saveRequest: Boolean,
collectionsType: Object, collectionsType: { type: Object, default: () => {} },
picked: Object, picked: { type: Object, default: () => {} },
}, },
data() { data() {
return { return {

View File

@@ -3,10 +3,16 @@
<div class="transition duration-150 ease-in-out row-wrapper"> <div class="transition duration-150 ease-in-out row-wrapper">
<div> <div>
<button class="icon" @click="toggleShowChildren"> <button class="icon" @click="toggleShowChildren">
<i class="material-icons" v-show="!showChildren && !isFiltered">arrow_right</i> <i v-show="!showChildren && !isFiltered" class="material-icons"
<i class="material-icons" v-show="showChildren || isFiltered">arrow_drop_down</i> >arrow_right</i
>
<i v-show="showChildren || isFiltered" class="material-icons"
>arrow_drop_down</i
>
<i v-if="isSelected" class="text-green-400 material-icons">check_circle</i> <i v-if="isSelected" class="text-green-400 material-icons"
>check_circle</i
>
<i v-else class="material-icons">folder_open</i> <i v-else class="material-icons">folder_open</i>
<span>{{ folder.name ? folder.name : folder.title }}</span> <span>{{ folder.name ? folder.name : folder.title }}</span>
@@ -15,8 +21,8 @@
<v-popover v-if="!saveRequest"> <v-popover v-if="!saveRequest">
<button <button
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'" v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
class="tooltip-target icon"
v-tooltip.left="$t('more')" v-tooltip.left="$t('more')"
class="tooltip-target icon"
> >
<i class="material-icons">more_vert</i> <i class="material-icons">more_vert</i>
</button> </button>
@@ -24,9 +30,9 @@
<div> <div>
<button <button
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'" v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
v-close-popover
class="icon" class="icon"
@click="$emit('add-folder', { folder, path: folderPath })" @click="$emit('add-folder', { folder, path: folderPath })"
v-close-popover
> >
<i class="material-icons">create_new_folder</i> <i class="material-icons">create_new_folder</i>
<span>{{ $t("new_folder") }}</span> <span>{{ $t("new_folder") }}</span>
@@ -35,9 +41,11 @@
<div> <div>
<button <button
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'" v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
class="icon"
@click="$emit('edit-folder', { folder, folderIndex, collectionIndex })"
v-close-popover v-close-popover
class="icon"
@click="
$emit('edit-folder', { folder, folderIndex, collectionIndex })
"
> >
<i class="material-icons">edit</i> <i class="material-icons">edit</i>
<span>{{ $t("edit") }}</span> <span>{{ $t("edit") }}</span>
@@ -46,9 +54,9 @@
<div> <div>
<button <button
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'" v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
v-close-popover
class="icon" class="icon"
@click="confirmRemove = true" @click="confirmRemove = true"
v-close-popover
> >
<i class="material-icons">delete</i> <i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span> <span>{{ $t("delete") }}</span>
@@ -69,8 +77,8 @@
:folder-index="subFolderIndex" :folder-index="subFolderIndex"
:collection-index="collectionIndex" :collection-index="collectionIndex"
:doc="doc" :doc="doc"
:saveRequest="saveRequest" :save-request="saveRequest"
:collectionsType="collectionsType" :collections-type="collectionsType"
:folder-path="`${folderPath}/${subFolderIndex}`" :folder-path="`${folderPath}/${subFolderIndex}`"
:picked="picked" :picked="picked"
@add-folder="$emit('add-folder', $event)" @add-folder="$emit('add-folder', $event)"
@@ -96,8 +104,8 @@
:folder-name="folder.name" :folder-name="folder.name"
:request-index="request.id" :request-index="request.id"
:doc="doc" :doc="doc"
:saveRequest="saveRequest" :save-request="saveRequest"
:collectionsType="collectionsType" :collections-type="collectionsType"
:picked="picked" :picked="picked"
@edit-request="$emit('edit-request', $event)" @edit-request="$emit('edit-request', $event)"
@select="$emit('select', $event)" @select="$emit('select', $event)"
@@ -112,7 +120,10 @@
" "
> >
<li class="flex ml-8 border-l border-brdColor"> <li class="flex ml-8 border-l border-brdColor">
<p class="info"><i class="material-icons">not_interested</i> {{ $t("folder_empty") }}</p> <p class="info">
<i class="material-icons">not_interested</i>
{{ $t("folder_empty") }}
</p>
</li> </li>
</ul> </ul>
</div> </div>
@@ -126,20 +137,20 @@
</template> </template>
<script> <script>
import * as team_utils from "~/helpers/teams/utils" import * as teamUtils from "~/helpers/teams/utils"
export default { export default {
name: "folder", name: "Folder",
props: { props: {
folder: Object, folder: { type: Object, default: () => {} },
folderIndex: Number, folderIndex: { type: Number, default: null },
collectionIndex: Number, collectionIndex: { type: Number, default: null },
folderPath: String, folderPath: { type: String, default: null },
doc: Boolean, doc: Boolean,
saveRequest: Boolean, saveRequest: Boolean,
isFiltered: Boolean, isFiltered: Boolean,
collectionsType: Object, collectionsType: { type: Object, default: () => {} },
picked: Object, picked: { type: Object, default: () => {} },
}, },
data() { data() {
return { return {
@@ -172,10 +183,10 @@ export default {
this.showChildren = !this.showChildren this.showChildren = !this.showChildren
}, },
removeFolder() { removeFolder() {
if (this.collectionsType.selectedTeam.myRole != "VIEWER") { if (this.collectionsType.selectedTeam.myRole !== "VIEWER") {
team_utils teamUtils
.deleteCollection(this.$apollo, this.folder.id) .deleteCollection(this.$apollo, this.folder.id)
.then((data) => { .then(() => {
// Result // Result
this.$toast.success(this.$t("deleted"), { this.$toast.success(this.$t("deleted"), {
icon: "delete", icon: "delete",

View File

@@ -3,27 +3,32 @@
<div class="transition duration-150 ease-in-out row-wrapper"> <div class="transition duration-150 ease-in-out row-wrapper">
<div> <div>
<button <button
v-tooltip="!doc ? $t('use_request') : ''"
class="icon" class="icon"
@click="!doc ? selectRequest() : {}" @click="!doc ? selectRequest() : {}"
v-tooltip="!doc ? $t('use_request') : ''"
> >
<i v-if="isSelected" class="mx-3 text-green-400 material-icons">check_circle</i> <i v-if="isSelected" class="mx-3 text-green-400 material-icons"
>check_circle</i
>
<span v-else :class="getRequestLabelColor(request.method)">{{ request.method }}</span> <span v-else :class="getRequestLabelColor(request.method)">{{
request.method
}}</span>
<span>{{ request.name }}</span> <span>{{ request.name }}</span>
</button> </button>
</div> </div>
<v-popover v-if="!saveRequest"> <v-popover v-if="!saveRequest">
<button <button
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'" v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
class="tooltip-target icon"
v-tooltip="$t('more')" v-tooltip="$t('more')"
class="tooltip-target icon"
> >
<i class="material-icons">more_vert</i> <i class="material-icons">more_vert</i>
</button> </button>
<template slot="popover"> <template slot="popover">
<div> <div>
<button <button
v-close-popover
class="icon" class="icon"
@click=" @click="
$emit('edit-request', { $emit('edit-request', {
@@ -34,14 +39,13 @@
requestIndex, requestIndex,
}) })
" "
v-close-popover
> >
<i class="material-icons">edit</i> <i class="material-icons">edit</i>
<span>{{ $t("edit") }}</span> <span>{{ $t("edit") }}</span>
</button> </button>
</div> </div>
<div> <div>
<button class="icon" @click="confirmRemove = true" v-close-popover> <button v-close-popover class="icon" @click="confirmRemove = true">
<i class="material-icons">delete</i> <i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span> <span>{{ $t("delete") }}</span>
</button> </button>
@@ -61,15 +65,16 @@
<script> <script>
export default { export default {
props: { props: {
request: Object, request: { type: Object, default: () => {} },
collectionIndex: Number, collectionIndex: { type: Number, default: null },
folderIndex: Number, folderIndex: { type: Number, default: null },
folderName: String, folderName: { type: String, default: null },
// eslint-disable-next-line vue/require-default-prop
requestIndex: [Number, String], requestIndex: [Number, String],
doc: Boolean, doc: Boolean,
saveRequest: Boolean, saveRequest: Boolean,
collectionsType: Object, collectionsType: { type: Object, default: () => {} },
picked: Object, picked: { type: Object, default: () => {} },
}, },
data() { data() {
return { return {
@@ -102,7 +107,8 @@ export default {
requestID: this.requestIndex, requestID: this.requestIndex,
}, },
}) })
else this.$store.commit("postwoman/selectRequest", { request: this.request }) else
this.$store.commit("postwoman/selectRequest", { request: this.request })
}, },
removeRequest() { removeRequest() {
this.$emit("remove-request", { this.$emit("remove-request", {
@@ -112,7 +118,10 @@ export default {
}) })
}, },
getRequestLabelColor(method) { getRequestLabelColor(method) {
return this.requestMethodLabels[method.toLowerCase()] || this.requestMethodLabels.default return (
this.requestMethodLabels[method.toLowerCase()] ||
this.requestMethodLabels.default
)
}, },
}, },
} }

View File

@@ -6,17 +6,24 @@
( (
<span v-for="(field, index) in fieldArgs" :key="index"> <span v-for="(field, index) in fieldArgs" :key="index">
{{ field.name }}: {{ field.name }}:
<GraphqlTypeLink :gqlType="field.type" :jumpTypeCallback="jumpTypeCallback" /> <GraphqlTypeLink
:gql-type="field.type"
:jump-type-callback="jumpTypeCallback"
/>
<span v-if="index !== fieldArgs.length - 1"> , </span> <span v-if="index !== fieldArgs.length - 1"> , </span>
</span> </span>
) </span ) </span
>: >:
<GraphqlTypeLink :gqlType="gqlField.type" :jumpTypeCallback="jumpTypeCallback" /> <GraphqlTypeLink
:gql-type="gqlField.type"
:jump-type-callback="jumpTypeCallback"
/>
</div> </div>
<div class="mt-2 text-fgLightColor field-desc" v-if="gqlField.description"> <div v-if="gqlField.description" class="mt-2 text-fgLightColor field-desc">
{{ gqlField.description }} {{ gqlField.description }}
</div> </div>
<div <div
v-if="gqlField.isDeprecated"
class=" class="
inline-block inline-block
px-4 px-4
@@ -29,7 +36,6 @@
rounded-lg rounded-lg
field-deprecated field-deprecated
" "
v-if="gqlField.isDeprecated"
> >
{{ $t("deprecated") }} {{ $t("deprecated") }}
</div> </div>
@@ -38,8 +44,14 @@
<div class="px-4 border-l-2 border-acColor"> <div class="px-4 border-l-2 border-acColor">
<div v-for="(field, index) in fieldArgs" :key="index"> <div v-for="(field, index) in fieldArgs" :key="index">
{{ field.name }}: {{ field.name }}:
<GraphqlTypeLink :gqlType="field.type" :jumpTypeCallback="jumpTypeCallback" /> <GraphqlTypeLink
<div class="mt-2 text-fgLightColor field-desc" v-if="field.description"> :gql-type="field.type"
:jump-type-callback="jumpTypeCallback"
/>
<div
v-if="field.description"
class="mt-2 text-fgLightColor field-desc"
>
{{ field.description }} {{ field.description }}
</div> </div>
</div> </div>
@@ -48,18 +60,11 @@
</div> </div>
</template> </template>
<style scoped lang="scss">
.field-highlighted {
@apply border-b-2;
@apply border-acColor;
}
</style>
<script> <script>
export default { export default {
props: { props: {
gqlField: Object, gqlField: { type: Object, default: () => {} },
jumpTypeCallback: Function, jumpTypeCallback: { type: Function, default: () => {} },
isHighlighted: { type: Boolean, default: false }, isHighlighted: { type: Boolean, default: false },
}, },
computed: { computed: {
@@ -73,3 +78,10 @@ export default {
}, },
} }
</script> </script>
<style scoped lang="scss">
.field-highlighted {
@apply border-b-2;
@apply border-acColor;
}
</style>

View File

@@ -4,27 +4,15 @@
</div> </div>
</template> </template>
<style scoped lang="scss">
.show-if-initialized {
&.initialized {
@apply opacity-100;
}
& > * {
@apply transition-none;
}
}
</style>
<script> <script>
import ace from "ace-builds" import ace from "ace-builds"
import "ace-builds/webpack-resolver" import "ace-builds/webpack-resolver"
import "ace-builds/src-noconflict/ext-language_tools" import "ace-builds/src-noconflict/ext-language_tools"
import "ace-builds/src-noconflict/mode-graphqlschema" import "ace-builds/src-noconflict/mode-graphqlschema"
import { defineGQLLanguageMode } from "~/helpers/syntax/gqlQueryLangMode"
import * as gql from "graphql" import * as gql from "graphql"
import { getAutocompleteSuggestions } from "graphql-language-service-interface" import { getAutocompleteSuggestions } from "graphql-language-service-interface"
import { defineGQLLanguageMode } from "~/helpers/syntax/gqlQueryLangMode"
import debounce from "~/helpers/utils/debounce" import debounce from "~/helpers/utils/debounce"
export default { export default {
@@ -44,7 +32,7 @@ export default {
}, },
options: { options: {
type: Object, type: Object,
default: {}, default: () => {},
}, },
styles: { styles: {
type: String, type: String,
@@ -84,7 +72,7 @@ export default {
mounted() { mounted() {
defineGQLLanguageMode(ace) defineGQLLanguageMode(ace)
let langTools = ace.require("ace/ext/language_tools") const langTools = ace.require("ace/ext/language_tools")
const editor = ace.edit(this.$refs.editor, { const editor = ace.edit(this.$refs.editor, {
mode: `ace/mode/gql-query`, mode: `ace/mode/gql-query`,
@@ -108,12 +96,22 @@ export default {
}) })
const completer = { const completer = {
getCompletions: (editor, _session, { row, column }, _prefix, callback) => { getCompletions: (
editor,
_session,
{ row, column },
_prefix,
callback
) => {
if (this.validationSchema) { if (this.validationSchema) {
const completions = getAutocompleteSuggestions(this.validationSchema, editor.getValue(), { const completions = getAutocompleteSuggestions(
line: row, this.validationSchema,
character: column, editor.getValue(),
}) {
line: row,
character: column,
}
)
callback( callback(
null, null,
@@ -165,10 +163,14 @@ export default {
this.parseContents(this.value) this.parseContents(this.value)
}, },
beforeDestroy() {
this.editor.destroy()
},
methods: { methods: {
prettifyQuery() { prettifyQuery() {
try { try {
this.value = gql.print(gql.parse(this.editor.getValue())) this.$emit("update-query", gql.print(gql.parse(this.editor.getValue())))
} catch (e) { } catch (e) {
this.$toast.error(`${this.$t("gql_prettify_invalid_query")}`, { this.$toast.error(`${this.$t("gql_prettify_invalid_query")}`, {
icon: "error", icon: "error",
@@ -180,9 +182,12 @@ export default {
if (this.theme) { if (this.theme) {
return this.theme return this.theme
} }
const strip = (str) => str.replace(/#/g, "").replace(/ /g, "").replace(/"/g, "") const strip = (str) =>
str.replace(/#/g, "").replace(/ /g, "").replace(/"/g, "")
return strip( return strip(
window.getComputedStyle(document.documentElement).getPropertyValue("--editor-theme") window
.getComputedStyle(document.documentElement)
.getPropertyValue("--editor-theme")
) )
}, },
@@ -198,12 +203,14 @@ export default {
if (this.validationSchema) { if (this.validationSchema) {
this.editor.session.setAnnotations( this.editor.session.setAnnotations(
gql.validate(this.validationSchema, doc).map(({ locations, message }) => ({ gql
row: locations[0].line - 1, .validate(this.validationSchema, doc)
column: locations[0].column - 1, .map(({ locations, message }) => ({
text: message, row: locations[0].line - 1,
type: "error", column: locations[0].column - 1,
})) text: message,
type: "error",
}))
) )
} }
} catch (e) { } catch (e) {
@@ -221,9 +228,17 @@ export default {
} }
}, 2000), }, 2000),
}, },
beforeDestroy() {
this.editor.destroy()
},
} }
</script> </script>
<style scoped lang="scss">
.show-if-initialized {
&.initialized {
@apply opacity-100;
}
& > * {
@apply transition-none;
}
}
</style>

View File

@@ -1,65 +1,79 @@
<template> <template>
<div :id="`type_${gqlType.name}`" class="p-2 m-2"> <div :id="`type_${gqlType.name}`" class="p-2 m-2">
<div class="font-bold type-title" :class="{ 'type-highlighted': isHighlighted }"> <div
class="font-bold type-title"
:class="{ 'type-highlighted': isHighlighted }"
>
<span v-if="isInput" class="font-normal text-acColor">input </span> <span v-if="isInput" class="font-normal text-acColor">input </span>
<span v-else-if="isInterface" class="font-normal text-acColor">interface </span> <span v-else-if="isInterface" class="font-normal text-acColor"
>interface
</span>
<span v-else-if="isEnum" class="font-normal text-acColor">enum </span> <span v-else-if="isEnum" class="font-normal text-acColor">enum </span>
{{ gqlType.name }} {{ gqlType.name }}
</div> </div>
<div class="mt-2 text-fgLightColor type-desc" v-if="gqlType.description"> <div v-if="gqlType.description" class="mt-2 text-fgLightColor type-desc">
{{ gqlType.description }} {{ gqlType.description }}
</div> </div>
<div v-if="interfaces.length > 0" class="mb-2"> <div v-if="interfaces.length > 0" class="mb-2">
<h5>{{ $t("interfaces") }}</h5> <h5>{{ $t("interfaces") }}</h5>
<div v-for="gqlInterface in interfaces" :key="gqlInterface.name" class="m-2 ml-4"> <div
<GraphqlTypeLink :gqlType="gqlInterface" :jumpTypeCallback="jumpTypeCallback" /> v-for="gqlInterface in interfaces"
:key="gqlInterface.name"
class="m-2 ml-4"
>
<GraphqlTypeLink
:gql-type="gqlInterface"
:jump-type-callback="jumpTypeCallback"
/>
</div> </div>
</div> </div>
<div v-if="children.length > 0" class="mb-2"> <div v-if="children.length > 0" class="mb-2">
<h5>{{ $t("children") }}</h5> <h5>{{ $t("children") }}</h5>
<div v-for="child in children" :key="child.name" class="m-2 ml-4"> <div v-for="child in children" :key="child.name" class="m-2 ml-4">
<GraphqlTypeLink :gqlType="child" :jumpTypeCallback="jumpTypeCallback" /> <GraphqlTypeLink
:gql-type="child"
:jump-type-callback="jumpTypeCallback"
/>
</div> </div>
</div> </div>
<div v-if="gqlType.getFields"> <div v-if="gqlType.getFields">
<h5>{{ $t("fields") }}</h5> <h5>{{ $t("fields") }}</h5>
<div v-for="field in gqlType.getFields()" :key="field.name"> <div v-for="field in gqlType.getFields()" :key="field.name">
<GraphqlField <GraphqlField
:gqlField="field" :gql-field="field"
:isHighlighted="isFieldHighlighted({ field })" :is-highlighted="isFieldHighlighted({ field })"
:jumpTypeCallback="jumpTypeCallback" :jump-type-callback="jumpTypeCallback"
/> />
</div> </div>
</div> </div>
<div v-if="isEnum"> <div v-if="isEnum">
<h5>{{ $t("values") }}</h5> <h5>{{ $t("values") }}</h5>
<div :key="value.name" v-for="value in gqlType.getValues()" class="m-4" v-text="value.name" /> <div
v-for="value in gqlType.getValues()"
:key="value.name"
class="m-4"
v-text="value.name"
></div>
</div> </div>
</div> </div>
</template> </template>
<style scoped lang="scss">
.type-highlighted {
@apply text-acColor;
}
</style>
<script> <script>
import { GraphQLEnumType, GraphQLInputObjectType, GraphQLInterfaceType } from "graphql" import {
GraphQLEnumType,
GraphQLInputObjectType,
GraphQLInterfaceType,
} from "graphql"
export default { export default {
props: { props: {
// eslint-disable-next-line vue/require-default-prop, vue/require-prop-types
gqlType: {}, gqlType: {},
gqlTypes: Array, gqlTypes: { type: Array, default: () => [] },
jumpTypeCallback: Function, jumpTypeCallback: { type: Function, default: () => {} },
isHighlighted: { type: Boolean, default: false }, isHighlighted: { type: Boolean, default: false },
highlightedFields: { type: Array, default: () => [] }, highlightedFields: { type: Array, default: () => [] },
}, },
methods: {
isFieldHighlighted({ field }) {
return !!this.highlightedFields.find(({ name }) => name === field.name)
},
},
computed: { computed: {
isInput() { isInput() {
return this.gqlType instanceof GraphQLInputObjectType return this.gqlType instanceof GraphQLInputObjectType
@@ -75,9 +89,21 @@ export default {
}, },
children() { children() {
return this.gqlTypes.filter( return this.gqlTypes.filter(
(type) => type.getInterfaces && type.getInterfaces().includes(this.gqlType) (type) =>
type.getInterfaces && type.getInterfaces().includes(this.gqlType)
) )
}, },
}, },
methods: {
isFieldHighlighted({ field }) {
return !!this.highlightedFields.find(({ name }) => name === field.name)
},
},
} }
</script> </script>
<style scoped lang="scss">
.type-highlighted {
@apply text-acColor;
}
</style>

View File

@@ -1,11 +1,11 @@
<template> <template>
<AppSection icon="history" :label="$t('history')" ref="history" no-legend> <AppSection ref="history" icon="history" :label="$t('history')" no-legend>
<div class="show-on-large-screen"> <div class="show-on-large-screen">
<input <input
v-model="filterText"
aria-label="Search" aria-label="Search"
type="search" type="search"
:placeholder="$t('search')" :placeholder="$t('search')"
v-model="filterText"
class="rounded-t-lg" class="rounded-t-lg"
/> />
</div> </div>
@@ -16,9 +16,9 @@
<ul v-for="(entry, index) in filteredHistory" :key="`entry-${index}`"> <ul v-for="(entry, index) in filteredHistory" :key="`entry-${index}`">
<HistoryRestCard <HistoryRestCard
v-if="page == 'rest'" v-if="page == 'rest'"
:entry="entry"
:id="index" :id="index"
:showMore="showMore" :entry="entry"
:show-more="showMore"
@toggle-star="toggleStar(entry)" @toggle-star="toggleStar(entry)"
@delete-entry="deleteHistory(entry)" @delete-entry="deleteHistory(entry)"
@use-entry="useHistory(entry)" @use-entry="useHistory(entry)"
@@ -26,21 +26,24 @@
<HistoryGraphqlCard <HistoryGraphqlCard
v-if="page == 'graphql'" v-if="page == 'graphql'"
:entry="entry" :entry="entry"
:showMore="showMore" :show-more="showMore"
@toggle-star="toggleStar(entry)" @toggle-star="toggleStar(entry)"
@delete-entry="deleteHistory(entry)" @delete-entry="deleteHistory(entry)"
@use-entry="useHistory(entry)" @use-entry="useHistory(entry)"
/> />
</ul> </ul>
</div> </div>
<p :class="{ hidden: filteredHistory.length != 0 || history.length === 0 }" class="info"> <p
:class="{ hidden: filteredHistory.length != 0 || history.length === 0 }"
class="info"
>
{{ $t("nothing_found") }} "{{ filterText }}" {{ $t("nothing_found") }} "{{ filterText }}"
</p> </p>
<p v-if="history.length === 0" class="info"> <p v-if="history.length === 0" class="info">
<i class="material-icons">schedule</i> {{ $t("history_empty") }} <i class="material-icons">schedule</i> {{ $t("history_empty") }}
</p> </p>
<div v-if="history.length !== 0" class="rounded-b-lg bg-bgDarkColor"> <div v-if="history.length !== 0" class="rounded-b-lg bg-bgDarkColor">
<div class="row-wrapper" v-if="!isClearingHistory"> <div v-if="!isClearingHistory" class="row-wrapper">
<button <button
data-testid="clear_history" data-testid="clear_history"
class="icon" class="icon"
@@ -60,22 +63,24 @@
</i> </i>
</button> </button>
</div> </div>
<div class="row-wrapper" v-else> <div v-else class="row-wrapper">
<p class="info"><i class="material-icons">help_outline</i> {{ $t("are_you_sure") }}</p> <p class="info">
<i class="material-icons">help_outline</i> {{ $t("are_you_sure") }}
</p>
<div> <div>
<button <button
v-tooltip="$t('yes')"
data-testid="confirm_clear_history" data-testid="confirm_clear_history"
class="icon" class="icon"
@click="clearHistory" @click="clearHistory"
v-tooltip="$t('yes')"
> >
<i class="material-icons">done</i> <i class="material-icons">done</i>
</button> </button>
<button <button
v-tooltip="$t('no')"
data-testid="reject_clear_history" data-testid="reject_clear_history"
class="icon" class="icon"
@click="disableHistoryClearing" @click="disableHistoryClearing"
v-tooltip="$t('no')"
> >
<i class="material-icons">close</i> <i class="material-icons">close</i>
</button> </button>
@@ -85,6 +90,134 @@
</AppSection> </AppSection>
</template> </template>
<script>
import { fb } from "~/helpers/fb"
const updateOnLocalStorage = (propertyName, property) =>
window.localStorage.setItem(propertyName, JSON.stringify(property))
export default {
props: {
page: { type: String, default: null },
},
data() {
return {
history:
fb.currentUser !== null
? this.page === "rest"
? fb.currentHistory
: fb.currentGraphqlHistory
: JSON.parse(
window.localStorage.getItem(
this.page === "rest" ? "history" : "graphqlHistory"
)
) || [],
filterText: "",
showFilter: false,
isClearingHistory: false,
showMore: false,
}
},
computed: {
filteredHistory() {
const filteringHistory =
fb.currentUser !== null
? this.page === "rest"
? fb.currentHistory
: fb.currentGraphqlHistory
: JSON.parse(
window.localStorage.getItem(
this.page === "rest" ? "history" : "graphqlHistory"
)
) || []
return filteringHistory.filter((entry) => {
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)
})
})
},
},
methods: {
async clearHistory() {
if (fb.currentUser !== null) {
this.page === "rest"
? await fb.clearHistory()
: await fb.clearGraphqlHistory()
}
this.history = []
this.filterText = ""
this.disableHistoryClearing()
updateOnLocalStorage(
this.page === "rest" ? "history" : "graphqlHistory",
this.history
)
this.$toast.error(this.$t("history_deleted"), {
icon: "delete",
})
},
useHistory(entry) {
this.$emit("useHistory", entry)
},
async deleteHistory(entry) {
if (this.history.length === 0) {
this.filterText = ""
}
if (fb.currentUser !== null) {
await (this.page === "rest"
? fb.deleteHistory(entry)
: fb.deleteGraphqlHistory(entry))
this.history = fb.currentHistory
updateOnLocalStorage(
this.page === "rest" ? "history" : "graphqlHistory",
this.history
)
} else {
this.history.splice(this.history.indexOf(entry), 1)
updateOnLocalStorage(
this.page === "rest" ? "history" : "graphqlHistory",
this.history
)
}
this.$toast.error(this.$t("deleted"), {
icon: "delete",
})
},
addEntry(entry) {
this.history.push(entry)
updateOnLocalStorage(
this.page === "rest" ? "history" : "graphqlHistory",
this.history
)
},
enableHistoryClearing() {
if (!this.history || !this.history.length) return
this.isClearingHistory = true
},
disableHistoryClearing() {
this.isClearingHistory = false
},
toggleCollapse() {
this.showMore = !this.showMore
},
async toggleStar(entry) {
if (fb.currentUser !== null) {
this.page === "rest"
? await fb.toggleStar(entry, !entry.star)
: await fb.toggleGraphqlHistoryStar(entry, !entry.star)
}
entry.star = !entry.star
updateOnLocalStorage(
this.page === "rest" ? "history" : "graphqlHistory",
this.history
)
},
},
}
</script>
<style scoped lang="scss"> <style scoped lang="scss">
.virtual-list { .virtual-list {
max-height: calc(100vh - 270px); max-height: calc(100vh - 270px);
@@ -105,106 +238,3 @@ ol {
} }
} }
</style> </style>
<script>
import { fb } from "~/helpers/fb"
const updateOnLocalStorage = (propertyName, property) =>
window.localStorage.setItem(propertyName, JSON.stringify(property))
export default {
props: {
page: String,
},
data() {
return {
history:
fb.currentUser !== null
? fb.currentHistory
: JSON.parse(
window.localStorage.getItem(this.page == "rest" ? "history" : "graphqlHistory")
) || [],
filterText: "",
showFilter: false,
isClearingHistory: false,
showMore: false,
}
},
computed: {
filteredHistory() {
this.history =
fb.currentUser !== null
? this.page == "rest"
? fb.currentHistory
: fb.currentGraphqlHistory
: JSON.parse(
window.localStorage.getItem(this.page == "rest" ? "history" : "graphqlHistory")
) || []
return this.history.filter((entry) => {
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)
})
})
},
},
methods: {
async clearHistory() {
if (fb.currentUser !== null) {
this.page == "rest" ? await fb.clearHistory() : await fb.clearGraphqlHistory()
}
this.history = []
this.filterText = ""
this.disableHistoryClearing()
updateOnLocalStorage(this.page == "rest" ? "history" : "graphqlHistory", this.history)
this.$toast.error(this.$t("history_deleted"), {
icon: "delete",
})
},
useHistory(entry) {
this.$emit("useHistory", entry)
},
async deleteHistory(entry) {
if (this.history.length === 0) {
this.filterText = ""
}
if (fb.currentUser !== null) {
await (this.page == "rest" ? fb.deleteHistory(entry) : fb.deleteGraphqlHistory(entry))
this.history = fb.currentHistory
updateOnLocalStorage(this.page == "rest" ? "history" : "graphqlHistory", this.history)
} else {
this.history.splice(this.history.indexOf(entry), 1)
updateOnLocalStorage(this.page == "rest" ? "history" : "graphqlHistory", this.history)
}
this.$toast.error(this.$t("deleted"), {
icon: "delete",
})
},
addEntry(entry) {
this.history.push(entry)
updateOnLocalStorage(this.page == "rest" ? "history" : "graphqlHistory", this.history)
},
enableHistoryClearing() {
if (!this.history || !this.history.length) return
this.isClearingHistory = true
},
disableHistoryClearing() {
this.isClearingHistory = false
},
toggleCollapse() {
this.showMore = !this.showMore
},
async toggleStar(entry) {
if (fb.currentUser !== null) {
this.page == "rest"
? await fb.toggleStar(entry, !entry.star)
: await fb.toggleGraphqlHistoryStar(entry, !entry.star)
}
entry.star = !entry.star
updateOnLocalStorage(this.page == "rest" ? "history" : "graphqlHistory", this.history)
},
},
}
</script>

View File

@@ -12,7 +12,7 @@ const styles = {
// TODO: probably have to use a more global state for `test` // TODO: probably have to use a more global state for `test`
export default function runTestScriptWithVariables(script, variables) { export default function runTestScriptWithVariables(script, variables) {
let pw = { const pw = {
_errors: [], _errors: [],
_testReports: [], _testReports: [],
_report: "", _report: "",
@@ -29,6 +29,7 @@ export default function runTestScriptWithVariables(script, variables) {
Object.assign(pw, variables) Object.assign(pw, variables)
// run pre-request script within this function so that it has access to the pw object. // run pre-request script within this function so that it has access to the pw object.
// eslint-disable-next-line no-new-func
new Function("pw", script)(pw) new Function("pw", script)(pw)
// //
const testReports = pw._testReports.map((item) => { const testReports = pw._testReports.map((item) => {
@@ -80,6 +81,7 @@ class Expectation {
} }
} }
} }
_fmtNot(message) { _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) // 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) { if (this.not === true) {
@@ -88,62 +90,102 @@ class Expectation {
return message.replace("(not)", "") return message.replace("(not)", "")
} }
} }
_fail(message) { _fail(message) {
return this._testReports.push({ result: FAIL, message }) return this._testReports.push({ result: FAIL, message })
} }
_pass() { _pass() {
return this._testReports.push({ result: PASS }) return this._testReports.push({ result: PASS })
} }
// TEST METHODS DEFINED BELOW // TEST METHODS DEFINED BELOW
// these are the usual methods that would follow expect(...) // these are the usual methods that would follow expect(...)
toBe(value) { toBe(value) {
return this._satisfies(value) return this._satisfies(value)
? this._pass()
: this._fail(this._fmtNot(`Expected ${this.expectValue} (not)to be ${value}`))
}
toHaveProperty(value) {
return this._satisfies(Object.prototype.hasOwnProperty.call(this.expectValue, value), true)
? this._pass() ? this._pass()
: this._fail( : this._fail(
this._fmtNot(`Expected object ${this.expectValue} to (not)have property ${value}`) this._fmtNot(`Expected ${this.expectValue} (not)to be ${value}`)
) )
} }
toHaveProperty(value) {
return this._satisfies(
Object.prototype.hasOwnProperty.call(this.expectValue, value),
true
)
? this._pass()
: this._fail(
this._fmtNot(
`Expected object ${this.expectValue} to (not)have property ${value}`
)
)
}
toBeLevel2xx() { toBeLevel2xx() {
const code = parseInt(this.expectValue, 10) const code = parseInt(this.expectValue, 10)
if (Number.isNaN(code)) { 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, true) return this._satisfies(code >= 200 && code < 300, true)
? this._pass() ? 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() { toBeLevel3xx() {
const code = parseInt(this.expectValue, 10) const code = parseInt(this.expectValue, 10)
if (Number.isNaN(code)) { 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, true) return this._satisfies(code >= 300 && code < 400, true)
? this._pass() ? 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() { toBeLevel4xx() {
const code = parseInt(this.expectValue, 10) const code = parseInt(this.expectValue, 10)
if (Number.isNaN(code)) { 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, true) return this._satisfies(code >= 400 && code < 500, true)
? this._pass() ? 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() { toBeLevel5xx() {
const code = parseInt(this.expectValue, 10) const code = parseInt(this.expectValue, 10)
if (Number.isNaN(code)) { 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, true) return this._satisfies(code >= 500 && code < 600, true)
? this._pass() ? 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`
)
)
} }
toHaveLength(expectedLength) { toHaveLength(expectedLength) {
const actualLength = this.expectValue.length const actualLength = this.expectValue.length
return this._satisfies(actualLength, expectedLength) return this._satisfies(actualLength, expectedLength)
@@ -154,6 +196,7 @@ class Expectation {
) )
) )
} }
toBeType(expectedType) { toBeType(expectedType) {
const actualType = typeof this.expectValue const actualType = typeof this.expectValue
if ( if (
@@ -177,7 +220,9 @@ class Expectation {
return this._satisfies(actualType, expectedType) return this._satisfies(actualType, expectedType)
? this._pass() ? this._pass()
: this._fail( : this._fail(
this._fmtNot(`Expected type to be "${expectedType}" but actual type was "${actualType}"`) this._fmtNot(
`Expected type to be "${expectedType}" but actual type was "${actualType}"`
)
) )
} }
} }

View File

@@ -1,10 +1,10 @@
export default function getEnvironmentVariablesFromScript(script) { export default function getEnvironmentVariablesFromScript(script) {
let _variables = {} const _variables = {}
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.
let pw = { const pw = {
environment: { environment: {
set: (key, value) => (_variables[key] = value), set: (key, value) => (_variables[key] = value),
}, },
@@ -15,6 +15,7 @@ export default function getEnvironmentVariablesFromScript(script) {
} }
// run pre-request script within this function so that it has access to the pw object. // run pre-request script within this function so that it has access to the pw object.
// eslint-disable-next-line no-new-func
new Function("pw", script)(pw) new Function("pw", script)(pw)
} catch (_e) {} } catch (_e) {}

View File

@@ -1,4 +1,3 @@
import { ApolloClient } from "@apollo/client/core"
import gql from "graphql-tag" import gql from "graphql-tag"
import { BehaviorSubject } from "rxjs" import { BehaviorSubject } from "rxjs"
@@ -95,7 +94,9 @@ export async function getLiveTeamMembersList(apollo, teamID) {
}) })
.subscribe(({ data }) => { .subscribe(({ data }) => {
subject.next( subject.next(
subject.value.filter((member) => member.user.uid !== data.teamMemberAdded.user.uid) subject.value.filter(
(member) => member.user.uid !== data.teamMemberAdded.user.uid
)
) )
}) })
@@ -112,22 +113,22 @@ export async function getLiveTeamMembersList(apollo, teamID) {
return subject return subject
} }
export async function createTeam(apollo, name) { export function createTeam(apollo, name) {
return apollo.mutate({ return apollo.mutate({
mutation: gql` mutation: gql`
mutation($name: String!) { mutation ($name: String!) {
createTeam(name: $name) { createTeam(name: $name) {
name name
} }
} }
`, `,
variables: { variables: {
name: name, name,
}, },
}) })
} }
export async function addTeamMemberByEmail(apollo, userRole, userEmail, teamID) { export function addTeamMemberByEmail(apollo, userRole, userEmail, teamID) {
return apollo.mutate({ return apollo.mutate({
mutation: gql` mutation: gql`
mutation addTeamMemberByEmail( mutation addTeamMemberByEmail(
@@ -135,20 +136,24 @@ export async function addTeamMemberByEmail(apollo, userRole, userEmail, teamID)
$userEmail: String! $userEmail: String!
$teamID: String! $teamID: String!
) { ) {
addTeamMemberByEmail(userRole: $userRole, userEmail: $userEmail, teamID: $teamID) { addTeamMemberByEmail(
userRole: $userRole
userEmail: $userEmail
teamID: $teamID
) {
role role
} }
} }
`, `,
variables: { variables: {
userRole: userRole, userRole,
userEmail: userEmail, userEmail,
teamID: teamID, teamID,
}, },
}) })
} }
export async function updateTeamMemberRole(apollo, userID, newRole, teamID) { export function updateTeamMemberRole(apollo, userID, newRole, teamID) {
return apollo.mutate({ return apollo.mutate({
mutation: gql` mutation: gql`
mutation updateTeamMemberRole( mutation updateTeamMemberRole(
@@ -156,20 +161,24 @@ export async function updateTeamMemberRole(apollo, userID, newRole, teamID) {
$userUid: String! $userUid: String!
$teamID: String! $teamID: String!
) { ) {
updateTeamMemberRole(newRole: $newRole, userUid: $userUid, teamID: $teamID) { updateTeamMemberRole(
newRole: $newRole
userUid: $userUid
teamID: $teamID
) {
role role
} }
} }
`, `,
variables: { variables: {
newRole: newRole, newRole,
userUid: userID, userUid: userID,
teamID: teamID, teamID,
}, },
}) })
} }
export async function renameTeam(apollo, name, teamID) { export function renameTeam(apollo, name, teamID) {
return apollo.mutate({ return apollo.mutate({
mutation: gql` mutation: gql`
mutation renameTeam($newName: String!, $teamID: String!) { mutation renameTeam($newName: String!, $teamID: String!) {
@@ -180,12 +189,12 @@ export async function renameTeam(apollo, name, teamID) {
`, `,
variables: { variables: {
newName: name, newName: name,
teamID: teamID, teamID,
}, },
}) })
} }
export async function removeTeamMember(apollo, userID, teamID) { export function removeTeamMember(apollo, userID, teamID) {
return apollo.mutate({ return apollo.mutate({
mutation: gql` mutation: gql`
mutation removeTeamMember($userUid: String!, $teamID: String!) { mutation removeTeamMember($userUid: String!, $teamID: String!) {
@@ -194,47 +203,47 @@ export async function removeTeamMember(apollo, userID, teamID) {
`, `,
variables: { variables: {
userUid: userID, userUid: userID,
teamID: teamID, teamID,
}, },
}) })
} }
export async function deleteTeam(apollo, teamID) { export async function deleteTeam(apollo, teamID) {
let response = undefined let response
while (true) { while (true) {
response = await apollo.mutate({ response = await apollo.mutate({
mutation: gql` mutation: gql`
mutation($teamID: String!) { mutation ($teamID: String!) {
deleteTeam(teamID: $teamID) deleteTeam(teamID: $teamID)
} }
`, `,
variables: { variables: {
teamID: teamID, teamID,
}, },
}) })
if (response != undefined) break if (response !== undefined) break
} }
return response return response
} }
export async function exitTeam(apollo, teamID) { export function exitTeam(apollo, teamID) {
apollo.mutate({ apollo.mutate({
mutation: gql` mutation: gql`
mutation($teamID: String!) { mutation ($teamID: String!) {
leaveTeam(teamID: $teamID) leaveTeam(teamID: $teamID)
} }
`, `,
variables: { variables: {
teamID: teamID, teamID,
}, },
}) })
} }
export async function rootCollectionsOfTeam(apollo, teamID) { export async function rootCollectionsOfTeam(apollo, teamID) {
var collections = [] const collections = []
var cursor = "" let cursor = ""
while (true) { while (true) {
var response = await apollo.query({ const response = await apollo.query({
query: gql` query: gql`
query rootCollectionsOfTeam($teamID: String!, $cursor: String!) { query rootCollectionsOfTeam($teamID: String!, $cursor: String!) {
rootCollectionsOfTeam(teamID: $teamID, cursor: $cursor) { rootCollectionsOfTeam(teamID: $teamID, cursor: $cursor) {
@@ -244,12 +253,12 @@ export async function rootCollectionsOfTeam(apollo, teamID) {
} }
`, `,
variables: { variables: {
teamID: teamID, teamID,
cursor: cursor, cursor,
}, },
fetchPolicy: "no-cache", fetchPolicy: "no-cache",
}) })
if (response.data.rootCollectionsOfTeam.length == 0) break if (response.data.rootCollectionsOfTeam.length === 0) break
response.data.rootCollectionsOfTeam.forEach((collection) => { response.data.rootCollectionsOfTeam.forEach((collection) => {
collections.push(collection) collections.push(collection)
}) })
@@ -259,8 +268,8 @@ export async function rootCollectionsOfTeam(apollo, teamID) {
} }
export async function getCollectionChildren(apollo, collectionID) { export async function getCollectionChildren(apollo, collectionID) {
var children = [] const children = []
var response = await apollo.query({ const response = await apollo.query({
query: gql` query: gql`
query getCollectionChildren($collectionID: String!) { query getCollectionChildren($collectionID: String!) {
collection(collectionID: $collectionID) { collection(collectionID: $collectionID) {
@@ -272,7 +281,7 @@ export async function getCollectionChildren(apollo, collectionID) {
} }
`, `,
variables: { variables: {
collectionID: collectionID, collectionID,
}, },
fetchPolicy: "no-cache", fetchPolicy: "no-cache",
}) })
@@ -283,10 +292,10 @@ export async function getCollectionChildren(apollo, collectionID) {
} }
export async function getCollectionRequests(apollo, collectionID) { export async function getCollectionRequests(apollo, collectionID) {
var requests = [] const requests = []
var cursor = "" let cursor = ""
while (true) { while (true) {
var response = await apollo.query({ const response = await apollo.query({
query: gql` query: gql`
query getCollectionRequests($collectionID: String!, $cursor: String) { query getCollectionRequests($collectionID: String!, $cursor: String) {
requestsInCollection(collectionID: $collectionID, cursor: $cursor) { requestsInCollection(collectionID: $collectionID, cursor: $cursor) {
@@ -297,8 +306,8 @@ export async function getCollectionRequests(apollo, collectionID) {
} }
`, `,
variables: { variables: {
collectionID: collectionID, collectionID,
cursor: cursor, cursor,
}, },
fetchPolicy: "no-cache", fetchPolicy: "no-cache",
}) })
@@ -316,11 +325,11 @@ export async function getCollectionRequests(apollo, collectionID) {
} }
export async function renameCollection(apollo, title, id) { export async function renameCollection(apollo, title, id) {
let response = undefined let response
while (true) { while (true) {
response = await apollo.mutate({ response = await apollo.mutate({
mutation: gql` mutation: gql`
mutation($newTitle: String!, $collectionID: String!) { mutation ($newTitle: String!, $collectionID: String!) {
renameCollection(newTitle: $newTitle, collectionID: $collectionID) { renameCollection(newTitle: $newTitle, collectionID: $collectionID) {
id id
} }
@@ -331,17 +340,17 @@ export async function renameCollection(apollo, title, id) {
collectionID: id, collectionID: id,
}, },
}) })
if (response != undefined) break if (response !== undefined) break
} }
return response return response
} }
export async function updateRequest(apollo, request, requestName, requestID) { export async function updateRequest(apollo, request, requestName, requestID) {
let response = undefined let response
while (true) { while (true) {
response = await apollo.mutate({ response = await apollo.mutate({
mutation: gql` mutation: gql`
mutation($data: UpdateTeamRequestInput!, $requestID: String!) { mutation ($data: UpdateTeamRequestInput!, $requestID: String!) {
updateRequest(data: $data, requestID: $requestID) { updateRequest(data: $data, requestID: $requestID) {
id id
} }
@@ -352,21 +361,24 @@ export async function updateRequest(apollo, request, requestName, requestID) {
request: JSON.stringify(request), request: JSON.stringify(request),
title: requestName, title: requestName,
}, },
requestID: requestID, requestID,
}, },
}) })
if (response != undefined) break if (response !== undefined) break
} }
return response return response
} }
export async function addChildCollection(apollo, title, id) { export async function addChildCollection(apollo, title, id) {
let response = undefined let response
while (true) { while (true) {
response = await apollo.mutate({ response = await apollo.mutate({
mutation: gql` mutation: gql`
mutation($childTitle: String!, $collectionID: String!) { mutation ($childTitle: String!, $collectionID: String!) {
createChildCollection(childTitle: $childTitle, collectionID: $collectionID) { createChildCollection(
childTitle: $childTitle
collectionID: $collectionID
) {
id id
} }
} }
@@ -376,17 +388,17 @@ export async function addChildCollection(apollo, title, id) {
collectionID: id, collectionID: id,
}, },
}) })
if (response != undefined) break if (response !== undefined) break
} }
return response return response
} }
export async function deleteCollection(apollo, id) { export async function deleteCollection(apollo, id) {
let response = undefined let response
while (true) { while (true) {
response = await apollo.mutate({ response = await apollo.mutate({
mutation: gql` mutation: gql`
mutation($collectionID: String!) { mutation ($collectionID: String!) {
deleteCollection(collectionID: $collectionID) deleteCollection(collectionID: $collectionID)
} }
`, `,
@@ -394,54 +406,60 @@ export async function deleteCollection(apollo, id) {
collectionID: id, collectionID: id,
}, },
}) })
if (response != undefined) break if (response !== undefined) break
} }
return response return response
} }
export async function deleteRequest(apollo, requestID) { export async function deleteRequest(apollo, requestID) {
let response = undefined let response
while (true) { while (true) {
response = await apollo.mutate({ response = await apollo.mutate({
mutation: gql` mutation: gql`
mutation($requestID: String!) { mutation ($requestID: String!) {
deleteRequest(requestID: $requestID) deleteRequest(requestID: $requestID)
} }
`, `,
variables: { variables: {
requestID: requestID, requestID,
}, },
}) })
if (response != undefined) break if (response !== undefined) break
} }
return response return response
} }
export async function createNewRootCollection(apollo, title, id) { export async function createNewRootCollection(apollo, title, id) {
let response = undefined let response
while (true) { while (true) {
response = await apollo.mutate({ response = await apollo.mutate({
mutation: gql` mutation: gql`
mutation($title: String!, $teamID: String!) { mutation ($title: String!, $teamID: String!) {
createRootCollection(title: $title, teamID: $teamID) { createRootCollection(title: $title, teamID: $teamID) {
id id
} }
} }
`, `,
variables: { variables: {
title: title, title,
teamID: id, teamID: id,
}, },
}) })
if (response != undefined) break if (response !== undefined) break
} }
return response return response
} }
export async function saveRequestAsTeams(apollo, request, title, teamID, collectionID) { export async function saveRequestAsTeams(
apollo,
request,
title,
teamID,
collectionID
) {
await apollo.mutate({ await apollo.mutate({
mutation: gql` mutation: gql`
mutation($data: CreateTeamRequestInput!, $collectionID: String!) { mutation ($data: CreateTeamRequestInput!, $collectionID: String!) {
createRequestInCollection(data: $data, collectionID: $collectionID) { createRequestInCollection(data: $data, collectionID: $collectionID) {
collection { collection {
id id
@@ -454,11 +472,11 @@ export async function saveRequestAsTeams(apollo, request, title, teamID, collect
} }
`, `,
variables: { variables: {
collectionID: collectionID, collectionID,
data: { data: {
teamID: teamID, teamID,
title: title, title,
request: request, request,
}, },
}, },
}) })
@@ -467,7 +485,10 @@ export async function saveRequestAsTeams(apollo, request, title, teamID, collect
export async function overwriteRequestTeams(apollo, request, title, requestID) { export async function overwriteRequestTeams(apollo, request, title, requestID) {
await apollo.mutate({ await apollo.mutate({
mutation: gql` mutation: gql`
mutation updateRequest($data: UpdateTeamRequestInput!, $requestID: String!) { mutation updateRequest(
$data: UpdateTeamRequestInput!
$requestID: String!
) {
updateRequest(data: $data, requestID: $requestID) { updateRequest(data: $data, requestID: $requestID) {
id id
title title
@@ -475,20 +496,26 @@ export async function overwriteRequestTeams(apollo, request, title, requestID) {
} }
`, `,
variables: { variables: {
requestID: requestID, requestID,
data: { data: {
request: request, request,
title: title, title,
}, },
}, },
}) })
} }
export async function importFromMyCollections(apollo, collectionID, teamID) { export async function importFromMyCollections(apollo, collectionID, teamID) {
let response = await apollo.mutate({ const response = await apollo.mutate({
mutation: gql` mutation: gql`
mutation importFromMyCollections($fbCollectionPath: String!, $teamID: String!) { mutation importFromMyCollections(
importCollectionFromUserFirestore(fbCollectionPath: $fbCollectionPath, teamID: $teamID) { $fbCollectionPath: String!
$teamID: String!
) {
importCollectionFromUserFirestore(
fbCollectionPath: $fbCollectionPath
teamID: $teamID
) {
id id
title title
} }
@@ -496,14 +523,14 @@ export async function importFromMyCollections(apollo, collectionID, teamID) {
`, `,
variables: { variables: {
fbCollectionPath: collectionID, fbCollectionPath: collectionID,
teamID: teamID, teamID,
}, },
}) })
return response.data != null return response.data != null
} }
export async function importFromJSON(apollo, collections, teamID) { export async function importFromJSON(apollo, collections, teamID) {
let response = await apollo.mutate({ const response = await apollo.mutate({
mutation: gql` mutation: gql`
mutation importFromJSON($jsonString: String!, $teamID: String!) { mutation importFromJSON($jsonString: String!, $teamID: String!) {
importCollectionsFromJSON(jsonString: $jsonString, teamID: $teamID) importCollectionsFromJSON(jsonString: $jsonString, teamID: $teamID)
@@ -511,14 +538,14 @@ export async function importFromJSON(apollo, collections, teamID) {
`, `,
variables: { variables: {
jsonString: JSON.stringify(collections), jsonString: JSON.stringify(collections),
teamID: teamID, teamID,
}, },
}) })
return response.data != null return response.data != null
} }
export async function replaceWithJSON(apollo, collections, teamID) { export async function replaceWithJSON(apollo, collections, teamID) {
let response = await apollo.mutate({ const response = await apollo.mutate({
mutation: gql` mutation: gql`
mutation replaceWithJSON($jsonString: String!, $teamID: String!) { mutation replaceWithJSON($jsonString: String!, $teamID: String!) {
replaceCollectionsWithJSON(jsonString: $jsonString, teamID: $teamID) replaceCollectionsWithJSON(jsonString: $jsonString, teamID: $teamID)
@@ -526,21 +553,21 @@ export async function replaceWithJSON(apollo, collections, teamID) {
`, `,
variables: { variables: {
jsonString: JSON.stringify(collections), jsonString: JSON.stringify(collections),
teamID: teamID, teamID,
}, },
}) })
return response.data != null return response.data != null
} }
export async function exportAsJSON(apollo, teamID) { export async function exportAsJSON(apollo, teamID) {
let response = await apollo.query({ const response = await apollo.query({
query: gql` query: gql`
query exportAsJSON($teamID: String!) { query exportAsJSON($teamID: String!) {
exportCollectionsToJSON(teamID: $teamID) exportCollectionsToJSON(teamID: $teamID)
} }
`, `,
variables: { variables: {
teamID: teamID, teamID,
}, },
}) })
return response.data.exportCollectionsToJSON return response.data.exportCollectionsToJSON

View File

@@ -10,7 +10,7 @@ describe("debounce", () => {
expect(fn).not.toHaveBeenCalled() expect(fn).not.toHaveBeenCalled()
}) })
test("calls the function after the given timeout", async () => { test("calls the function after the given timeout", () => {
const fn = jest.fn() const fn = jest.fn()
jest.useFakeTimers() jest.useFakeTimers()
@@ -24,7 +24,7 @@ describe("debounce", () => {
expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 100) expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 100)
}) })
test("calls the function only one time within the timeframe", async () => { test("calls the function only one time within the timeframe", () => {
const fn = jest.fn() const fn = jest.fn()
const debFunc = debounce(fn, 1000) const debFunc = debounce(fn, 1000)

View File

@@ -3,6 +3,10 @@ import { map } from "rxjs/operators"
import assign from "lodash/assign" import assign from "lodash/assign"
import clone from "lodash/clone" import clone from "lodash/clone"
export type Dispatchers<StoreType> = {
[key: string]: (currentVal: StoreType, payload: any) => Partial<StoreType>
}
type Dispatch< type Dispatch<
StoreType, StoreType,
DispatchersType extends Dispatchers<StoreType>, DispatchersType extends Dispatchers<StoreType>,
@@ -12,21 +16,26 @@ type Dispatch<
payload: any payload: any
} }
export type Dispatchers<StoreType> = { export default class DispatchingStore<
[key: string]: (currentVal: StoreType, payload: any) => Partial<StoreType> StoreType,
} DispatchersType extends Dispatchers<StoreType>
> {
export default class DispatchingStore<StoreType, DispatchersType extends Dispatchers<StoreType>> {
#state$: BehaviorSubject<StoreType> #state$: BehaviorSubject<StoreType>
#dispatchers: Dispatchers<StoreType> #dispatchers: Dispatchers<StoreType>
#dispatches$: Subject<Dispatch<StoreType, DispatchersType, keyof DispatchersType>> = new Subject() #dispatches$: Subject<
Dispatch<StoreType, DispatchersType, keyof DispatchersType>
> = new Subject()
constructor(initialValue: StoreType, dispatchers: DispatchersType) { constructor(initialValue: StoreType, dispatchers: DispatchersType) {
this.#state$ = new BehaviorSubject(initialValue) this.#state$ = new BehaviorSubject(initialValue)
this.#dispatchers = dispatchers this.#dispatchers = dispatchers
this.#dispatches$ this.#dispatches$
.pipe(map(({ dispatcher, payload }) => this.#dispatchers[dispatcher](this.value, payload))) .pipe(
map(({ dispatcher, payload }) =>
this.#dispatchers[dispatcher](this.value, payload)
)
)
.subscribe((val) => { .subscribe((val) => {
const data = clone(this.value) const data = clone(this.value)
assign(data, val) assign(data, val)
@@ -47,8 +56,12 @@ export default class DispatchingStore<StoreType, DispatchersType extends Dispatc
return this.#dispatches$ return this.#dispatches$
} }
dispatch({ dispatcher, payload }: Dispatch<StoreType, DispatchersType, keyof DispatchersType>) { dispatch({
if (!this.#dispatchers[dispatcher]) throw new Error(`Undefined dispatch type '${dispatcher}'`) dispatcher,
payload,
}: Dispatch<StoreType, DispatchersType, keyof DispatchersType>) {
if (!this.#dispatchers[dispatcher])
throw new Error(`Undefined dispatch type '${dispatcher}'`)
this.#dispatches$.next({ dispatcher, payload }) this.#dispatches$.next({ dispatcher, payload })
} }

View File

@@ -2,18 +2,18 @@
<div class="page"> <div class="page">
<div class="content"> <div class="content">
<div class="page-columns inner-left"> <div class="page-columns inner-left">
<AppSection :label="$t('endpoint')" ref="endpoint" no-legend> <AppSection ref="endpoint" :label="$t('endpoint')" no-legend>
<ul> <ul>
<li> <li>
<label for="url">{{ $t("url") }}</label> <label for="url">{{ $t("url") }}</label>
<input <input
id="url" id="url"
type="url"
v-model="url" v-model="url"
type="url"
spellcheck="false" spellcheck="false"
@keyup.enter="onPollSchemaClick()"
class="md:rounded-bl-lg" class="md:rounded-bl-lg"
:placeholder="$t('url')" :placeholder="$t('url')"
@keyup.enter="onPollSchemaClick()"
/> />
</li> </li>
<div> <div>
@@ -22,8 +22,8 @@
<button <button
id="get" id="get"
name="get" name="get"
@click="onPollSchemaClick"
class="rounded-b-lg md:rounded-bl-none md:rounded-br-lg" class="rounded-b-lg md:rounded-bl-none md:rounded-br-lg"
@click="onPollSchemaClick"
> >
{{ !isPollingSchema ? $t("connect") : $t("disconnect") }} {{ !isPollingSchema ? $t("connect") : $t("disconnect") }}
<span <span
@@ -37,7 +37,7 @@
</ul> </ul>
</AppSection> </AppSection>
<AppSection :label="$t('headers')" ref="headers" no-legend> <AppSection ref="headers" :label="$t('headers')" no-legend>
<div class="flex flex-col"> <div class="flex flex-col">
<label>{{ $t("headers") }}</label> <label>{{ $t("headers") }}</label>
<ul v-if="headers.length !== 0"> <ul v-if="headers.length !== 0">
@@ -45,7 +45,11 @@
<div class="row-wrapper"> <div class="row-wrapper">
<label for="headerList">{{ $t("header_list") }}</label> <label for="headerList">{{ $t("header_list") }}</label>
<div> <div>
<button class="icon" @click="headers = []" v-tooltip.bottom="$t('clear')"> <button
v-tooltip.bottom="$t('clear')"
class="icon"
@click="headers = []"
>
<i class="material-icons">clear_all</i> <i class="material-icons">clear_all</i>
</button> </button>
</div> </div>
@@ -71,13 +75,13 @@
:source="commonHeaders" :source="commonHeaders"
:spellcheck="false" :spellcheck="false"
:value="header.key" :value="header.key"
autofocus
@input=" @input="
$store.commit('setGQLHeaderKey', { $store.commit('setGQLHeaderKey', {
index, index,
value: $event, value: $event,
}) })
" "
autofocus
/> />
</li> </li>
<li> <li>
@@ -85,25 +89,18 @@
:placeholder="$t('value_count', { count: index + 1 })" :placeholder="$t('value_count', { count: index + 1 })"
:name="`value ${index}`" :name="`value ${index}`"
:value="header.value" :value="header.value"
autofocus
@change=" @change="
$store.commit('setGQLHeaderValue', { $store.commit('setGQLHeaderValue', {
index, index,
value: $event.target.value, value: $event.target.value,
}) })
" "
autofocus
/> />
</li> </li>
<div> <div>
<li> <li>
<button <button
class="icon"
@click="
$store.commit('setActiveGQLHeader', {
index,
value: header.hasOwnProperty('active') ? !header.active : false,
})
"
v-tooltip.bottom="{ v-tooltip.bottom="{
content: header.hasOwnProperty('active') content: header.hasOwnProperty('active')
? header.active ? header.active
@@ -111,6 +108,15 @@
: $t('turn_on') : $t('turn_on')
: $t('turn_off'), : $t('turn_off'),
}" }"
class="icon"
@click="
$store.commit('setActiveGQLHeader', {
index,
value: header.hasOwnProperty('active')
? !header.active
: false,
})
"
> >
<i class="material-icons"> <i class="material-icons">
{{ {{
@@ -127,9 +133,9 @@
<div> <div>
<li> <li>
<button <button
v-tooltip.bottom="$t('delete')"
class="icon" class="icon"
@click="removeRequestHeader(index)" @click="removeRequestHeader(index)"
v-tooltip.bottom="$t('delete')"
> >
<i class="material-icons">delete</i> <i class="material-icons">delete</i>
</button> </button>
@@ -147,35 +153,37 @@
</div> </div>
</AppSection> </AppSection>
<AppSection :label="$t('schema')" ref="schema" no-legend> <AppSection ref="schema" :label="$t('schema')" no-legend>
<div class="row-wrapper"> <div class="row-wrapper">
<label>{{ $t("schema") }}</label> <label>{{ $t("schema") }}</label>
<div v-if="schema"> <div v-if="schema">
<button <button
class="icon"
@click="ToggleExpandResponse"
ref="ToggleExpandResponse" ref="ToggleExpandResponse"
v-tooltip="{ v-tooltip="{
content: !expandResponse ? $t('expand_response') : $t('collapse_response'), content: !expandResponse
? $t('expand_response')
: $t('collapse_response'),
}" }"
class="icon"
@click="ToggleExpandResponse"
> >
<i class="material-icons"> <i class="material-icons">
{{ !expandResponse ? "unfold_more" : "unfold_less" }} {{ !expandResponse ? "unfold_more" : "unfold_less" }}
</i> </i>
</button> </button>
<button <button
class="icon"
@click="downloadSchema"
ref="downloadSchema" ref="downloadSchema"
v-tooltip="$t('download_file')" v-tooltip="$t('download_file')"
class="icon"
@click="downloadSchema"
> >
<i class="material-icons">save_alt</i> <i class="material-icons">save_alt</i>
</button> </button>
<button <button
class="icon"
ref="copySchemaCode" ref="copySchemaCode"
@click="copySchema"
v-tooltip="$t('copy_schema')" v-tooltip="$t('copy_schema')"
class="icon"
@click="copySchema"
> >
<i class="material-icons">content_copy</i> <i class="material-icons">content_copy</i>
</button> </button>
@@ -198,45 +206,47 @@
/> />
<input <input
v-else v-else
ref="status"
class="rounded-b-lg missing-data-response" class="rounded-b-lg missing-data-response"
:value="$t('waiting_receive_schema')" :value="$t('waiting_receive_schema')"
ref="status"
name="status" name="status"
readonly readonly
type="text" type="text"
/> />
</AppSection> </AppSection>
<AppSection :label="$t('query')" ref="query" no-legend> <AppSection ref="query" :label="$t('query')" no-legend>
<div class="row-wrapper gqlRunQuery"> <div class="row-wrapper gqlRunQuery">
<label for="gqlQuery">{{ $t("query") }}</label> <label for="gqlQuery">{{ $t("query") }}</label>
<div> <div>
<button <button
v-tooltip.bottom="
`${$t('run_query')} (${getSpecialKey()}-Enter)`
"
@click="runQuery()" @click="runQuery()"
v-tooltip.bottom="`${$t('run_query')} (${getSpecialKey()}-Enter)`"
> >
<i class="material-icons">play_arrow</i> <i class="material-icons">play_arrow</i>
</button> </button>
<button <button
class="icon"
@click="copyQuery"
ref="copyQueryButton" ref="copyQueryButton"
v-tooltip="$t('copy_query')" v-tooltip="$t('copy_query')"
class="icon"
@click="copyQuery"
> >
<i class="material-icons">content_copy</i> <i class="material-icons">content_copy</i>
</button> </button>
<button <button
v-tooltip="`${$t('prettify_query')} (${getSpecialKey()}-P)`"
class="icon" class="icon"
@click="doPrettifyQuery" @click="doPrettifyQuery"
v-tooltip="`${$t('prettify_query')} (${getSpecialKey()}-P)`"
> >
<i class="material-icons">photo_filter</i> <i class="material-icons">photo_filter</i>
</button> </button>
<button <button
class="icon"
@click="saveRequest"
ref="saveRequest" ref="saveRequest"
v-tooltip.bottom="$t('save_to_collections')" v-tooltip.bottom="$t('save_to_collections')"
class="icon"
@click="saveRequest"
> >
<i class="material-icons">create_new_folder</i> <i class="material-icons">create_new_folder</i>
</button> </button>
@@ -245,7 +255,8 @@
<GraphqlQueryEditor <GraphqlQueryEditor
ref="queryEditor" ref="queryEditor"
v-model="gqlQueryString" v-model="gqlQueryString"
:onRunGQLQuery="runQuery" styles="rounded-b-lg"
:on-run-g-q-l-query="runQuery"
:options="{ :options="{
maxLines: responseBodyMaxLines, maxLines: responseBodyMaxLines,
minLines: 10, minLines: 10,
@@ -254,11 +265,11 @@
showPrintMargin: false, showPrintMargin: false,
useWorker: false, useWorker: false,
}" }"
styles="rounded-b-lg" @update-query="updateQuery"
/> />
</AppSection> </AppSection>
<AppSection :label="$t('variables')" ref="variables" no-legend> <AppSection ref="variables" :label="$t('variables')" no-legend>
<div class="flex flex-col"> <div class="flex flex-col">
<label>{{ $t("variables") }}</label> <label>{{ $t("variables") }}</label>
<SmartAceEditor <SmartAceEditor
@@ -277,27 +288,27 @@
</div> </div>
</AppSection> </AppSection>
<AppSection :label="$t('response')" ref="response" no-legend> <AppSection ref="response" :label="$t('response')" no-legend>
<div class="flex flex-col"> <div class="flex flex-col">
<label>{{ $t("response") }}</label> <label>{{ $t("response") }}</label>
<div class="row-wrapper"> <div class="row-wrapper">
<label for="responseField">{{ $t("response_body") }}</label> <label for="responseField">{{ $t("response_body") }}</label>
<div> <div>
<button <button
v-if="response"
ref="downloadResponse"
v-tooltip="$t('download_file')"
class="icon" class="icon"
@click="downloadResponse" @click="downloadResponse"
ref="downloadResponse"
v-if="response"
v-tooltip="$t('download_file')"
> >
<i class="material-icons">save_alt</i> <i class="material-icons">save_alt</i>
</button> </button>
<button <button
v-if="response"
ref="copyResponseButton"
v-tooltip="$t('copy_response')"
class="icon" class="icon"
@click="copyResponse" @click="copyResponse"
ref="copyResponseButton"
v-if="response"
v-tooltip="$t('copy_response')"
> >
<i class="material-icons">content_copy</i> <i class="material-icons">content_copy</i>
</button> </button>
@@ -321,9 +332,9 @@
/> />
<input <input
v-else v-else
ref="status"
class="rounded-b-lg missing-data-response" class="rounded-b-lg missing-data-response"
:value="$t('waiting_receive_response')" :value="$t('waiting_receive_response')"
ref="status"
name="status" name="status"
readonly readonly
type="text" type="text"
@@ -335,12 +346,12 @@
<aside class="sticky-inner inner-right lg:max-w-md"> <aside class="sticky-inner inner-right lg:max-w-md">
<SmartTabs> <SmartTabs>
<SmartTab :id="'docs'" :label="`Docs`" :selected="true"> <SmartTab :id="'docs'" :label="`Docs`" :selected="true">
<AppSection :label="$t('docs')" ref="docs" no-legend> <AppSection ref="docs" :label="$t('docs')" no-legend>
<section class="flex-col"> <section class="flex-col">
<input <input
v-model="graphqlFieldsFilterText"
type="text" type="text"
:placeholder="$t('search')" :placeholder="$t('search')"
v-model="graphqlFieldsFilterText"
class="rounded-t-lg" class="rounded-t-lg"
/> />
<SmartTabs ref="gqlTabs" styles="m-4"> <SmartTabs ref="gqlTabs" styles="m-4">
@@ -351,8 +362,14 @@
:label="$t('queries')" :label="$t('queries')"
:selected="true" :selected="true"
> >
<div v-for="field in filteredQueryFields" :key="field.name"> <div
<GraphqlField :gqlField="field" :jumpTypeCallback="handleJumpToType" /> v-for="field in filteredQueryFields"
:key="field.name"
>
<GraphqlField
:gql-field="field"
:jump-type-callback="handleJumpToType"
/>
</div> </div>
</SmartTab> </SmartTab>
@@ -361,8 +378,14 @@
:id="'mutations'" :id="'mutations'"
:label="$t('mutations')" :label="$t('mutations')"
> >
<div v-for="field in filteredMutationFields" :key="field.name"> <div
<GraphqlField :gqlField="field" :jumpTypeCallback="handleJumpToType" /> v-for="field in filteredMutationFields"
:key="field.name"
>
<GraphqlField
:gql-field="field"
:jump-type-callback="handleJumpToType"
/>
</div> </div>
</SmartTab> </SmartTab>
@@ -371,24 +394,37 @@
:id="'subscriptions'" :id="'subscriptions'"
:label="$t('subscriptions')" :label="$t('subscriptions')"
> >
<div v-for="field in filteredSubscriptionFields" :key="field.name"> <div
<GraphqlField :gqlField="field" :jumpTypeCallback="handleJumpToType" /> v-for="field in filteredSubscriptionFields"
:key="field.name"
>
<GraphqlField
:gql-field="field"
:jump-type-callback="handleJumpToType"
/>
</div> </div>
</SmartTab> </SmartTab>
<SmartTab <SmartTab
v-if="graphqlTypes.length > 0" v-if="graphqlTypes.length > 0"
:id="'types'" :id="'types'"
:label="$t('types')"
ref="typesTab" ref="typesTab"
:label="$t('types')"
> >
<div v-for="type in filteredGraphqlTypes" :key="type.name"> <div
v-for="type in filteredGraphqlTypes"
:key="type.name"
>
<GraphqlType <GraphqlType
:gqlType="type" :gql-type="type"
:gqlTypes="graphqlTypes" :gql-types="graphqlTypes"
:isHighlighted="isGqlTypeHighlighted({ gqlType: type })" :is-highlighted="
:highlightedFields="getGqlTypeHighlightedFields({ gqlType: type })" isGqlTypeHighlighted({ gqlType: type })
:jumpTypeCallback="handleJumpToType" "
:highlighted-fields="
getGqlTypeHighlightedFields({ gqlType: type })
"
:jump-type-callback="handleJumpToType"
/> />
</div> </div>
</SmartTab> </SmartTab>
@@ -411,9 +447,9 @@
<SmartTab :id="'history'" :label="$t('history')"> <SmartTab :id="'history'" :label="$t('history')">
<History <History
@useHistory="handleUseHistory"
ref="graphqlHistoryComponent" ref="graphqlHistoryComponent"
:page="'graphql'" :page="'graphql'"
@useHistory="handleUseHistory"
/> />
</SmartTab> </SmartTab>
@@ -433,23 +469,12 @@
</div> </div>
<CollectionsGraphqlSaveRequest <CollectionsGraphqlSaveRequest
:show="showSaveRequestModal" :show="showSaveRequestModal"
@hide-modal="hideRequestModal"
:editing-request="editRequest" :editing-request="editRequest"
@hide-modal="hideRequestModal"
/> />
</div> </div>
</template> </template>
<style scoped lang="scss">
.gqlTabs {
max-height: calc(100vh - 192px);
position: relative;
@apply overflow-auto;
}
.gqlRunQuery {
@apply mb-8;
}
</style>
<script> <script>
import * as gql from "graphql" import * as gql from "graphql"
import { commonHeaders } from "~/helpers/headers" import { commonHeaders } from "~/helpers/headers"
@@ -459,6 +484,12 @@ import { getSettingSubject } from "~/newstore/settings"
import { fb } from "~/helpers/fb" import { fb } from "~/helpers/fb"
export default { export default {
beforeRouteLeave(_to, _from, next) {
this.isPollingSchema = false
if (this.timeoutSubscription) clearTimeout(this.timeoutSubscription)
next()
},
data() { data() {
return { return {
commonHeaders, commonHeaders,
@@ -484,14 +515,10 @@ export default {
SCROLL_INTO_ENABLED: getSettingSubject("SCROLL_INTO_ENABLED"), SCROLL_INTO_ENABLED: getSettingSubject("SCROLL_INTO_ENABLED"),
} }
}, },
watch: { head() {
selectedRequest(newValue) { return {
if (!newValue) return title: `GraphQL • Hoppscotch`,
this.url = newValue.url }
this.gqlQueryString = newValue.query
this.headers = newValue.headers
this.variableString = newValue.variables
},
}, },
computed: { computed: {
selectedRequest() { selectedRequest() {
@@ -576,18 +603,26 @@ export default {
}, },
}, },
}, },
watch: {
selectedRequest(newValue) {
if (!newValue) return
this.url = newValue.url
this.gqlQueryString = newValue.query
this.headers = newValue.headers
this.variableString = newValue.variables
},
},
mounted() { mounted() {
if (this.$store.state.gql.schemaIntrospection && this.$store.state.gql.schema) { if (
const gqlSchema = gql.buildClientSchema(JSON.parse(this.$store.state.gql.schemaIntrospection)) this.$store.state.gql.schemaIntrospection &&
this.$store.state.gql.schema
) {
const gqlSchema = gql.buildClientSchema(
JSON.parse(this.$store.state.gql.schemaIntrospection)
)
this.getDocsFromSchema(gqlSchema) this.getDocsFromSchema(gqlSchema)
} }
}, },
beforeRouteLeave(_to, _from, next) {
this.isPollingSchema = false
if (this.timeoutSubscription) clearTimeout(this.timeoutSubscription)
next()
},
methods: { methods: {
hideRequestModal() { hideRequestModal() {
this.showSaveRequestModal = false this.showSaveRequestModal = false
@@ -641,7 +676,9 @@ export default {
const isFilterTextFoundInDescription = graphqlFieldObject.description const isFilterTextFoundInDescription = graphqlFieldObject.description
? graphqlFieldObject.description.toLowerCase().includes(normalizedText) ? graphqlFieldObject.description.toLowerCase().includes(normalizedText)
: false : false
const isFilterTextFoundInName = graphqlFieldObject.name.toLowerCase().includes(normalizedText) const isFilterTextFoundInName = graphqlFieldObject.name
.toLowerCase()
.includes(normalizedText)
return isFilterTextFoundInDescription || isFilterTextFoundInName return isFilterTextFoundInDescription || isFilterTextFoundInName
}, },
@@ -649,7 +686,10 @@ export default {
if (!filterText) return fields if (!filterText) return fields
return fields.filter((field) => return fields.filter((field) =>
this.isTextFoundInGraphqlFieldObject({ text: filterText, graphqlFieldObject: field }) this.isTextFoundInGraphqlFieldObject({
text: filterText,
graphqlFieldObject: field,
})
) )
}, },
getFilteredGraphqlTypes({ filterText, types }) { getFilteredGraphqlTypes({ filterText, types }) {
@@ -665,12 +705,13 @@ export default {
return true return true
} }
const isFilterTextMatchingAtLeastOneField = Object.values(type._fields || {}).some( const isFilterTextMatchingAtLeastOneField = Object.values(
(field) => type._fields || {}
this.isTextFoundInGraphqlFieldObject({ ).some((field) =>
text: filterText, this.isTextFoundInGraphqlFieldObject({
graphqlFieldObject: field, text: filterText,
}) graphqlFieldObject: field,
})
) )
return isFilterTextMatchingAtLeastOneField return isFilterTextMatchingAtLeastOneField
@@ -709,7 +750,10 @@ export default {
this.$toast.success(this.$t("copied_to_clipboard"), { this.$toast.success(this.$t("copied_to_clipboard"), {
icon: "done", icon: "done",
}) })
setTimeout(() => (this.$refs.copySchemaCode.innerHTML = this.copyButton), 1000) setTimeout(
() => (this.$refs.copySchemaCode.innerHTML = this.copyButton),
1000
)
}, },
copyQuery() { copyQuery() {
this.$refs.copyQueryButton.innerHTML = this.doneButton this.$refs.copyQueryButton.innerHTML = this.doneButton
@@ -722,7 +766,10 @@ export default {
this.$toast.success(this.$t("copied_to_clipboard"), { this.$toast.success(this.$t("copied_to_clipboard"), {
icon: "done", icon: "done",
}) })
setTimeout(() => (this.$refs.copyQueryButton.innerHTML = this.copyButton), 1000) setTimeout(
() => (this.$refs.copyQueryButton.innerHTML = this.copyButton),
1000
)
}, },
copyResponse() { copyResponse() {
this.$refs.copyResponseButton.innerHTML = this.doneButton this.$refs.copyResponseButton.innerHTML = this.doneButton
@@ -735,7 +782,10 @@ export default {
this.$toast.success(this.$t("copied_to_clipboard"), { this.$toast.success(this.$t("copied_to_clipboard"), {
icon: "done", icon: "done",
}) })
setTimeout(() => (this.$refs.copyResponseButton.innerHTML = this.copyButton), 1000) setTimeout(
() => (this.$refs.copyResponseButton.innerHTML = this.copyButton),
1000
)
}, },
async runQuery() { async runQuery() {
const startTime = Date.now() const startTime = Date.now()
@@ -748,14 +798,18 @@ export default {
if (this.SCROLL_INTO_ENABLED) this.scrollInto("response") if (this.SCROLL_INTO_ENABLED) this.scrollInto("response")
try { try {
let headers = {} const headers = {}
this.headers this.headers
.filter((item) => (item.hasOwnProperty("active") ? item.active == true : true)) .filter((item) =>
Object.prototype.hasOwnProperty.call(item, "active")
? item.active === true
: true
)
.forEach(({ key, value }) => { .forEach(({ key, value }) => {
headers[key] = value headers[key] = value
}) })
let variables = JSON.parse(this.variableString || "{}") const variables = JSON.parse(this.variableString || "{}")
const gqlQueryString = this.gqlQueryString const gqlQueryString = this.gqlQueryString
@@ -778,7 +832,9 @@ export default {
const res = await sendNetworkRequest(reqOptions) const res = await sendNetworkRequest(reqOptions)
// HACK: Temporary trailing null character issue from the extension fix // HACK: Temporary trailing null character issue from the extension fix
const responseText = new TextDecoder("utf-8").decode(res.data).replace(/\0+$/, "") const responseText = new TextDecoder("utf-8")
.decode(res.data)
.replace(/\0+$/, "")
this.response = JSON.stringify(JSON.parse(responseText), null, 2) this.response = JSON.stringify(JSON.parse(responseText), null, 2)
@@ -846,17 +902,23 @@ export default {
const typeMap = schema.getTypeMap() const typeMap = schema.getTypeMap()
const types = [] const types = []
const queryTypeName = schema.getQueryType() ? schema.getQueryType().name : "" const queryTypeName = schema.getQueryType()
const mutationTypeName = schema.getMutationType() ? schema.getMutationType().name : "" ? schema.getQueryType().name
: ""
const mutationTypeName = schema.getMutationType()
? schema.getMutationType().name
: ""
const subscriptionTypeName = schema.getSubscriptionType() const subscriptionTypeName = schema.getSubscriptionType()
? schema.getSubscriptionType().name ? schema.getSubscriptionType().name
: "" : ""
for (const typeName in typeMap) { for (const typeName in typeMap) {
let type = typeMap[typeName] const type = typeMap[typeName]
if ( if (
!type.name.startsWith("__") && !type.name.startsWith("__") &&
![queryTypeName, mutationTypeName, subscriptionTypeName].includes(type.name) && ![queryTypeName, mutationTypeName, subscriptionTypeName].includes(
type.name
) &&
(type instanceof gql.GraphQLObjectType || (type instanceof gql.GraphQLObjectType ||
type instanceof gql.GraphQLInputObjectType || type instanceof gql.GraphQLInputObjectType ||
type instanceof gql.GraphQLEnumType || type instanceof gql.GraphQLEnumType ||
@@ -887,9 +949,13 @@ export default {
query: gql.getIntrospectionQuery(), query: gql.getIntrospectionQuery(),
}) })
let headers = {} const headers = {}
this.headers this.headers
.filter((item) => (item.hasOwnProperty("active") ? item.active == true : true)) .filter((item) =>
Object.prototype.hasOwnProperty.call(item, "active")
? item.active === true
: true
)
.forEach(({ key, value }) => { .forEach(({ key, value }) => {
headers[key] = value headers[key] = value
}) })
@@ -907,7 +973,9 @@ export default {
const data = await sendNetworkRequest(reqOptions, this.$store) const data = await sendNetworkRequest(reqOptions, this.$store)
// HACK : Temporary trailing null character issue from the extension fix // HACK : Temporary trailing null character issue from the extension fix
const response = new TextDecoder("utf-8").decode(data.data).replace(/\0+$/, "") const response = new TextDecoder("utf-8")
.decode(data.data)
.replace(/\0+$/, "")
const introspectResponse = JSON.parse(response) const introspectResponse = JSON.parse(response)
const schema = gql.buildClientSchema(introspectResponse.data) const schema = gql.buildClientSchema(introspectResponse.data)
@@ -926,13 +994,16 @@ export default {
this.$refs.queryEditor.setValidationSchema(schema) this.$refs.queryEditor.setValidationSchema(schema)
this.$nuxt.$loading.finish() this.$nuxt.$loading.finish()
if (this.isPollingSchema) this.timeoutSubscription = setTimeout(this.pollSchema, 7000) if (this.isPollingSchema)
this.timeoutSubscription = setTimeout(this.pollSchema, 7000)
} catch (error) { } catch (error) {
this.$nuxt.$loading.finish() this.$nuxt.$loading.finish()
this.schema = `${error}. ${this.$t("check_console_details")}` this.schema = `${error}. ${this.$t("check_console_details")}`
this.$toast.error( this.$toast.error(
`${this.$t("graphql_introspect_failed")} ${this.$t("check_graphql_valid")}`, `${this.$t("graphql_introspect_failed")} ${this.$t(
"check_graphql_valid"
)}`,
{ {
icon: "error", icon: "error",
} }
@@ -959,9 +1030,13 @@ export default {
query: gql.getIntrospectionQuery(), query: gql.getIntrospectionQuery(),
}) })
let headers = {} const headers = {}
this.headers this.headers
.filter((item) => (item.hasOwnProperty("active") ? item.active == true : true)) .filter((item) =>
Object.prototype.hasOwnProperty.call(item, "active")
? item.active === true
: true
)
.forEach(({ key, value }) => { .forEach(({ key, value }) => {
headers[key] = value headers[key] = value
}) })
@@ -979,7 +1054,9 @@ export default {
const data = await sendNetworkRequest(reqOptions, this.$store) const data = await sendNetworkRequest(reqOptions, this.$store)
// HACK : Temporary trailing null character issue from the extension fix // HACK : Temporary trailing null character issue from the extension fix
const response = new TextDecoder("utf-8").decode(data.data).replace(/\0+$/, "") const response = new TextDecoder("utf-8")
.decode(data.data)
.replace(/\0+$/, "")
const introspectResponse = JSON.parse(response) const introspectResponse = JSON.parse(response)
const schema = gql.buildClientSchema(introspectResponse.data) const schema = gql.buildClientSchema(introspectResponse.data)
@@ -1006,7 +1083,9 @@ export default {
this.schema = `${error}. ${this.$t("check_console_details")}` this.schema = `${error}. ${this.$t("check_console_details")}`
this.$toast.error( this.$toast.error(
`${this.$t("graphql_introspect_failed")} ${this.$t("check_graphql_valid")}`, `${this.$t("graphql_introspect_failed")} ${this.$t(
"check_graphql_valid"
)}`,
{ {
icon: "error", icon: "error",
} }
@@ -1016,7 +1095,8 @@ export default {
}, },
ToggleExpandResponse() { ToggleExpandResponse() {
this.expandResponse = !this.expandResponse this.expandResponse = !this.expandResponse
this.responseBodyMaxLines = this.responseBodyMaxLines == Infinity ? 16 : Infinity this.responseBodyMaxLines =
this.responseBodyMaxLines === Infinity ? 16 : Infinity
}, },
downloadResponse() { downloadResponse() {
const dataToWrite = this.response const dataToWrite = this.response
@@ -1056,7 +1136,7 @@ export default {
this.$refs.downloadSchema.innerHTML = this.downloadButton this.$refs.downloadSchema.innerHTML = this.downloadButton
}, 1000) }, 1000)
}, },
addRequestHeader(index) { addRequestHeader() {
this.$store.commit("addGQLHeader", { this.$store.commit("addGQLHeader", {
key: "", key: "",
value: "", value: "",
@@ -1073,7 +1153,7 @@ export default {
action: { action: {
text: this.$t("undo"), text: this.$t("undo"),
duration: 4000, duration: 4000,
onClick: (e, toastObject) => { onClick: (_, toastObject) => {
this.headers = oldHeaders this.headers = oldHeaders
toastObject.remove() toastObject.remove()
}, },
@@ -1085,11 +1165,20 @@ export default {
behavior: "smooth", behavior: "smooth",
}) })
}, },
}, updateQuery(updatedQuery) {
head() { this.gqlQueryString = updatedQuery
return { },
title: `GraphQL • Hoppscotch`,
}
}, },
} }
</script> </script>
<style scoped lang="scss">
.gqlTabs {
max-height: calc(100vh - 192px);
position: relative;
@apply overflow-auto;
}
.gqlRunQuery {
@apply mb-8;
}
</style>