Merge pull request #1367 from hoppscotch/feat/gist

Collections and environments + GitHub gist
This commit is contained in:
Andrew Bastin
2020-12-08 23:08:24 -05:00
committed by GitHub
6 changed files with 261 additions and 12 deletions

View File

@@ -6,6 +6,44 @@
<div class="row-wrapper">
<h3 class="title">{{ $t("import_export") }} {{ $t("collections") }}</h3>
<div>
<v-popover>
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
<i class="material-icons">more_vert</i>
</button>
<template slot="popover">
<div>
<button class="icon" @click="readCollectionGist" v-close-popover>
<i class="material-icons">code</i>
<span>{{ $t("import_from_gist") }}</span>
</button>
</div>
<div
v-tooltip.bottom="{
content: !fb.currentUser
? $t('login_with_github_to') + $t('create_secret_gist')
: fb.currentUser.provider !== 'github.com'
? $t('login_with_github_to') + $t('create_secret_gist')
: null,
}"
>
<button
:disabled="
!fb.currentUser
? true
: fb.currentUser.provider !== 'github.com'
? true
: false
"
class="icon"
@click="createCollectionGist"
v-close-popover
>
<i class="material-icons">code</i>
<span>{{ $t("create_secret_gist") }}</span>
</button>
</div>
</template>
</v-popover>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
@@ -93,6 +131,57 @@ export default {
},
},
methods: {
async createCollectionGist() {
await this.$axios
.$post(
"https://api.github.com/gists",
{
files: {
"hoppscotch-collections.json": {
content: this.collectionJson,
},
},
},
{
headers: {
Authorization: `token ${fb.currentUser.accessToken}`,
Accept: "application/vnd.github.v3+json",
},
}
)
.then((response) => {
this.$toast.success(this.$t("gist_created"), {
icon: "done",
})
window.open(response.html_url)
})
.catch((error) => {
this.$toast.error(this.$t("something_went_wrong"), {
icon: "error",
})
console.log(error)
})
},
async readCollectionGist() {
let gist = prompt(this.$t("enter_gist_url"))
if (!gist) return
await this.$axios
.$get(`https://api.github.com/gists/${gist.split("/").pop()}`, {
headers: {
Accept: "application/vnd.github.v3+json",
},
})
.then((response) => {
let collections = JSON.parse(Object.values(response.files)[0].content)
this.$store.commit("postwoman/replaceCollections", collections)
this.fileImported()
this.syncToFBCollections()
})
.catch((error) => {
this.failedImport()
console.log(error)
})
},
hideModal() {
this.$emit("hide-modal")
},

View File

@@ -6,6 +6,44 @@
<div class="row-wrapper">
<h3 class="title">{{ $t("import_export") }} {{ $t("environments") }}</h3>
<div>
<v-popover>
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
<i class="material-icons">more_vert</i>
</button>
<template slot="popover">
<div>
<button class="icon" @click="readEnvironmentGist" v-close-popover>
<i class="material-icons">code</i>
<span>{{ $t("import_from_gist") }}</span>
</button>
</div>
<div
v-tooltip.bottom="{
content: !fb.currentUser
? $t('login_with_github_to') + $t('create_secret_gist')
: fb.currentUser.provider !== 'github.com'
? $t('login_with_github_to') + $t('create_secret_gist')
: null,
}"
>
<button
:disabled="
!fb.currentUser
? true
: fb.currentUser.provider !== 'github.com'
? true
: false
"
class="icon"
@click="createEnvironmentGist"
v-close-popover
>
<i class="material-icons">code</i>
<span>{{ $t("create_secret_gist") }}</span>
</button>
</div>
</template>
</v-popover>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
@@ -93,6 +131,57 @@ export default {
},
},
methods: {
async createEnvironmentGist() {
await this.$axios
.$post(
"https://api.github.com/gists",
{
files: {
"hoppscotch-environments.json": {
content: this.environmentJson,
},
},
},
{
headers: {
Authorization: `token ${fb.currentUser.accessToken}`,
Accept: "application/vnd.github.v3+json",
},
}
)
.then((response) => {
this.$toast.success(this.$t("gist_created"), {
icon: "done",
})
window.open(response.html_url)
})
.catch((error) => {
this.$toast.error(this.$t("something_went_wrong"), {
icon: "error",
})
console.log(error)
})
},
async readEnvironmentGist() {
let gist = prompt(this.$t("enter_gist_url"))
if (!gist) return
await this.$axios
.$get(`https://api.github.com/gists/${gist.split("/").pop()}`, {
headers: {
Accept: "application/vnd.github.v3+json",
},
})
.then((response) => {
let environments = JSON.parse(Object.values(response.files)[0].content)
this.$store.commit("postwoman/replaceEnvironments", environments)
this.fileImported()
this.syncToFBEnvironments()
})
.catch((error) => {
this.failedImport()
console.log(error)
})
},
hideModal() {
this.$emit("hide-modal")
},

View File

@@ -62,6 +62,7 @@ export default {
this.showLoginSuccess()
} catch (err) {
console.log(err)
// An error happened.
if (err.code === "auth/account-exists-with-different-credential") {
// Step 2.
@@ -80,22 +81,23 @@ export default {
// Asks the user their password.
// In real scenario, you should handle this asynchronously.
const password = promptUserForPassword() // TODO: implement promptUserForPassword.
const user = await fb.signInWithEmailAndPassword(email, password)
const user = await fb.signInWithEmailAndPassword(email, password)
await user.linkWithCredential(pendingCred)
this.showLoginSuccess()
return
}
this.$toast.info(`${this.$t("login_with")}`, {
this.$toast.info(`${this.$t("account_exists")}`, {
icon: "vpn_key",
duration: null,
closeOnSwipe: false,
action: {
text: this.$t("yes"),
onClick: async (e, toastObject) => {
const user = await fb.signInWithGithub()
const { user } = await fb.signInWithGithub()
await user.linkAndRetrieveDataWithCredential(pendingCred)
this.showLoginSuccess()
@@ -109,7 +111,9 @@ export default {
},
async signInWithGithub() {
try {
const { additionalUserInfo } = await fb.signInUserWithGithub()
const { credential, additionalUserInfo } = await fb.signInUserWithGithub()
fb.setProviderInfo(credential.providerId, credential.accessToken)
if (additionalUserInfo.isNewUser) {
this.$toast.info(`${this.$t("turn_on")} ${this.$t("sync")}`, {
@@ -131,6 +135,7 @@ export default {
this.showLoginSuccess()
} catch (err) {
console.log(err)
// An error happened.
if (err.code === "auth/account-exists-with-different-credential") {
// Step 2.
@@ -158,7 +163,7 @@ export default {
return
}
this.$toast.info(`${this.$t("login_with")}`, {
this.$toast.info(`${this.$t("account_exists")}`, {
icon: "vpn_key",
duration: null,
closeOnSwipe: false,

View File

@@ -205,7 +205,11 @@ describe("FirebaseInstance", () => {
const fbFunc = jest.spyOn(mockAuth, "signInWithPopup")
const fb = new FirebaseInstance(mocksdk, {
github: () => {},
github: () => {
return {
addScope: () => {}
}
},
})
signOutUser()
@@ -218,7 +222,11 @@ describe("FirebaseInstance", () => {
const fbFunc = jest.spyOn(mockAuth, "signInWithPopup")
const fb = new FirebaseInstance(mocksdk, {
github: () => {},
github: () => {
return {
addScope: () => {}
}
},
})
signOutUser()
@@ -231,7 +239,11 @@ describe("FirebaseInstance", () => {
const fbFunc = jest.spyOn(mockAuth, "signInWithPopup")
const fb = new FirebaseInstance(mocksdk, {
github: () => {},
github: () => {
return {
addScope: () => {}
}
},
})
signOutUser()
@@ -240,11 +252,35 @@ describe("FirebaseInstance", () => {
await expect(fb.signInUserWithGithub()).rejects.toEqual("test error")
})
test("adds 'repo gist' scope", async () => {
const fbFunc = jest.spyOn(mockAuth, "signInWithPopup")
const addScopeMock = jest.fn()
const fb = new FirebaseInstance(mocksdk, {
github: () => {
return {
addScope: addScopeMock
}
},
})
signOutUser()
fbFunc.mockImplementation(() => Promise.resolve("test"))
await fb.signInUserWithGithub()
expect(addScopeMock).toBeCalledWith("repo gist")
})
test("resolves the response the firebase request resolves", async () => {
const fbFunc = jest.spyOn(mockAuth, "signInWithPopup")
const fb = new FirebaseInstance(mocksdk, {
github: () => {},
github: () => {
return {
addScope: () => {}
}
},
})
signOutUser()

View File

@@ -36,6 +36,7 @@ export class FirebaseInstance {
this.app.auth().onAuthStateChanged((user) => {
if (user) {
this.currentUser = user
this.currentUser.providerData.forEach((profile) => {
let us = {
updatedOn: new Date(),
@@ -47,10 +48,15 @@ export class FirebaseInstance {
}
this.usersCollection
.doc(this.currentUser.uid)
.set(us)
.set(us, { merge: true })
.catch((e) => console.error("error updating", us, e))
})
this.usersCollection.doc(this.currentUser.uid).onSnapshot((doc) => {
this.currentUser.provider = doc.data().provider
this.currentUser.accessToken = doc.data().accessToken
})
this.usersCollection
.doc(this.currentUser.uid)
.collection("feeds")
@@ -131,7 +137,7 @@ export class FirebaseInstance {
}
async signInUserWithGithub() {
return await this.app.auth().signInWithPopup(this.authProviders.github())
return await this.app.auth().signInWithPopup(this.authProviders.github().addScope("repo gist"))
}
async signInWithEmailAndPassword(email, password) {
@@ -288,6 +294,24 @@ export class FirebaseInstance {
throw e
}
}
async setProviderInfo(id, token) {
const us = {
updatedOn: new Date(),
provider: id,
accessToken: token,
}
try {
await this.usersCollection
.doc(this.currentUser.uid)
.update(us)
.catch((e) => console.error("error updating", us, e))
} catch (e) {
console.error("error updating", ev, e)
throw e
}
}
}
export const fb = new FirebaseInstance(firebase.initializeApp(firebaseConfig), authProviders)

View File

@@ -294,5 +294,11 @@
"experiments": "Experiments",
"experiments_notice": "This is a collection of experiments we're working on that might turn out to be useful, fun, both, or neither. They're not final and may not be stable, so if something overly weird happens, don't panic. Just turn the dang thing off. Jokes aside, ",
"use_experimental_url_bar": "Use experimental URL bar with environment highlighting",
"select_environment": "Select environment"
"select_environment": "Select environment",
"login_with_github_to": "Login with GitHub to ",
"create_secret_gist": "Create secret Gist",
"gist_created": "Gist created",
"import_from_gist": "Import from Gist",
"enter_gist_url": "Enter Gist URL",
"account_exists": "Account exists with different credential - Login to link both accounts"
}