feat: implement environments for selfhosted (#30)

This commit is contained in:
Akash K
2023-03-08 16:47:29 +05:30
committed by GitHub
parent 40208a13e0
commit 80898407c3
23 changed files with 960 additions and 69 deletions

View File

@@ -0,0 +1,112 @@
import {
runMutation,
runGQLQuery,
runGQLSubscription,
} from "@hoppscotch/common/helpers/backend/GQLClient"
import {
CreateUserEnvironmentDocument,
CreateUserEnvironmentMutation,
CreateUserEnvironmentMutationVariables,
UpdateUserEnvironmentMutation,
UpdateUserEnvironmentMutationVariables,
UpdateUserEnvironmentDocument,
DeleteUserEnvironmentMutation,
DeleteUserEnvironmentMutationVariables,
DeleteUserEnvironmentDocument,
ClearGlobalEnvironmentsMutation,
ClearGlobalEnvironmentsMutationVariables,
ClearGlobalEnvironmentsDocument,
CreateUserGlobalEnvironmentMutation,
CreateUserGlobalEnvironmentMutationVariables,
CreateUserGlobalEnvironmentDocument,
GetGlobalEnvironmentsDocument,
GetGlobalEnvironmentsQueryVariables,
GetGlobalEnvironmentsQuery,
GetUserEnvironmentsDocument,
UserEnvironmentCreatedDocument,
UserEnvironmentUpdatedDocument,
UserEnvironmentDeletedDocument,
} from "./../../api/generated/graphql"
import { Environment } from "@hoppscotch/data"
export const createUserEnvironment = (name: string, variables: string) =>
runMutation<
CreateUserEnvironmentMutation,
CreateUserEnvironmentMutationVariables,
""
>(CreateUserEnvironmentDocument, {
name,
variables,
})()
export const updateUserEnvironment = (
id: string,
{ name, variables }: Environment
) =>
runMutation<
UpdateUserEnvironmentMutation,
UpdateUserEnvironmentMutationVariables,
""
>(UpdateUserEnvironmentDocument, {
id,
name,
variables: JSON.stringify(variables),
})
export const deleteUserEnvironment = (id: string) =>
runMutation<
DeleteUserEnvironmentMutation,
DeleteUserEnvironmentMutationVariables,
""
>(DeleteUserEnvironmentDocument, {
id,
})
export const clearGlobalEnvironmentVariables = (id: string) =>
runMutation<
ClearGlobalEnvironmentsMutation,
ClearGlobalEnvironmentsMutationVariables,
""
>(ClearGlobalEnvironmentsDocument, {
id,
})()
export const getUserEnvironments = () =>
runGQLQuery({
query: GetUserEnvironmentsDocument,
})
export const getGlobalEnvironments = () =>
runGQLQuery<
GetGlobalEnvironmentsQuery,
GetGlobalEnvironmentsQueryVariables,
"user_environment/user_env_does_not_exists"
>({
query: GetGlobalEnvironmentsDocument,
})
export const createUserGlobalEnvironment = (variables: string) =>
runMutation<
CreateUserGlobalEnvironmentMutation,
CreateUserGlobalEnvironmentMutationVariables,
""
>(CreateUserGlobalEnvironmentDocument, {
variables,
})()
export const runUserEnvironmentCreatedSubscription = () =>
runGQLSubscription({
query: UserEnvironmentCreatedDocument,
})
export const runUserEnvironmentUpdatedSubscription = () =>
runGQLSubscription({
query: UserEnvironmentUpdatedDocument,
})
export const runUserEnvironmentDeletedSubscription = () =>
runGQLSubscription({
query: UserEnvironmentDeletedDocument,
})

View File

@@ -0,0 +1,219 @@
import { authEvents$, def as platformAuth } from "@platform/auth"
import {
createEnvironment,
deleteEnvironment,
replaceEnvironments,
setGlobalEnvVariables,
updateEnvironment,
} from "@hoppscotch/common/newstore/environments"
import { EnvironmentsPlatformDef } from "@hoppscotch/common/src/platform/environments"
import { runGQLSubscription } from "@hoppscotch/common/helpers/backend/GQLClient"
import {
environmentsMapper,
globalEnvironmentMapper,
environnmentsSyncer,
} from "@platform/environments/environments.sync"
import * as E from "fp-ts/Either"
import { runDispatchWithOutSyncing } from "@lib/sync"
import {
createUserGlobalEnvironment,
getGlobalEnvironments,
getUserEnvironments,
runUserEnvironmentCreatedSubscription,
runUserEnvironmentDeletedSubscription,
runUserEnvironmentUpdatedSubscription,
} from "@platform/environments/environments.api"
export function initEnvironmentsSync() {
const currentUser$ = platformAuth.getCurrentUserStream()
environnmentsSyncer.startStoreSync()
environnmentsSyncer.setupSubscriptions(setupSubscriptions)
currentUser$.subscribe(async (user) => {
if (user) {
await loadAllEnvironments()
}
})
authEvents$.subscribe((event) => {
if (event.event == "login" || event.event == "token_refresh") {
environnmentsSyncer.startListeningToSubscriptions()
}
if (event.event == "logout") {
environnmentsSyncer.stopListeningToSubscriptions()
}
})
}
export const def: EnvironmentsPlatformDef = {
initEnvironmentsSync,
}
function setupSubscriptions() {
let subs: ReturnType<typeof runGQLSubscription>[1][] = []
const userEnvironmentCreatedSub = setupUserEnvironmentCreatedSubscription()
const userEnvironmentUpdatedSub = setupUserEnvironmentUpdatedSubscription()
const userEnvironmentDeletedSub = setupUserEnvironmentDeletedSubscription()
subs = [
userEnvironmentCreatedSub,
userEnvironmentUpdatedSub,
userEnvironmentDeletedSub,
]
return () => {
subs.forEach((sub) => sub.unsubscribe())
}
}
async function loadUserEnvironments() {
const res = await getUserEnvironments()
if (E.isRight(res)) {
const environments = res.right.me.environments
if (environments.length > 0) {
environments.forEach((env, index) => {
environmentsMapper.addEntry(index, env.id)
})
runDispatchWithOutSyncing(() => {
replaceEnvironments(
environments.map(({ id, variables, name }) => ({
id,
name,
variables: JSON.parse(variables),
}))
)
})
}
}
}
async function loadGlobalEnvironments() {
const res = await getGlobalEnvironments()
if (E.isRight(res)) {
const globalEnv = res.right.me.globalEnvironments
if (globalEnv) {
runDispatchWithOutSyncing(() => {
setGlobalEnvVariables(JSON.parse(globalEnv.variables))
})
globalEnvironmentMapper.addEntry(0, globalEnv.id)
}
} else if (res.left.error == "user_environment/user_env_does_not_exists") {
const res = await createUserGlobalEnvironment(JSON.stringify([]))
if (E.isRight(res)) {
const backendId = res.right.createUserGlobalEnvironment.id
globalEnvironmentMapper.addEntry(0, backendId)
}
}
}
async function loadAllEnvironments() {
await loadUserEnvironments()
await loadGlobalEnvironments()
}
function setupUserEnvironmentCreatedSubscription() {
const [userEnvironmentCreated$, userEnvironmentCreatedSub] =
runUserEnvironmentCreatedSubscription()
userEnvironmentCreated$.subscribe((res) => {
console.group("Subscription: User Environment Created")
console.log(res)
console.groupEnd()
if (E.isRight(res)) {
const { name, variables } = res.right.userEnvironmentCreated
if (name) {
runDispatchWithOutSyncing(() => {
createEnvironment(name, JSON.parse(variables))
})
}
}
})
return userEnvironmentCreatedSub
}
function setupUserEnvironmentUpdatedSubscription() {
const [userEnvironmentUpdated$, userEnvironmentUpdatedSub] =
runUserEnvironmentUpdatedSubscription()
userEnvironmentUpdated$.subscribe((res) => {
console.group("Subscription: User Environment Updated")
console.log(res)
console.groupEnd()
if (E.isRight(res)) {
const { name, variables, id, isGlobal } = res.right.userEnvironmentUpdated
// handle the case for global environments
if (isGlobal) {
runDispatchWithOutSyncing(() => {
setGlobalEnvVariables(JSON.parse(variables))
})
} else {
// handle the case for normal environments
const localIndex = environmentsMapper.getIndexByBackendId(id)
if (localIndex && name) {
runDispatchWithOutSyncing(() => {
updateEnvironment(localIndex, {
name,
variables: JSON.parse(variables),
})
})
}
}
}
})
return userEnvironmentUpdatedSub
}
function setupUserEnvironmentDeletedSubscription() {
console.log("setting up user environments for user deleted")
const [userEnvironmentDeleted$, userEnvironmentDeletedSub] =
runUserEnvironmentDeletedSubscription()
userEnvironmentDeleted$.subscribe((res) => {
console.group("Subscription: User Environment Deleted")
console.log(res)
console.groupEnd()
if (E.isRight(res)) {
const { id } = res.right.userEnvironmentDeleted
const localIndex = environmentsMapper.getIndexByBackendId(id)
if (localIndex) {
runDispatchWithOutSyncing(() => {
deleteEnvironment(localIndex)
})
environmentsMapper.removeEntry(id)
} else {
console.log("could not find the localIndex")
// TODO:
// handle order of events
// eg: update coming before create
// skipping for this release
}
}
})
return userEnvironmentDeletedSub
}

View File

@@ -0,0 +1,112 @@
import { environmentsStore } from "@hoppscotch/common/newstore/environments"
import {
getSettingSubject,
settingsStore,
} from "@hoppscotch/common/newstore/settings"
import { getSyncInitFunction } from "../../lib/sync"
import * as E from "fp-ts/Either"
import { StoreSyncDefinitionOf } from "../../lib/sync"
import { createMapper } from "../../lib/sync/mapper"
import {
clearGlobalEnvironmentVariables,
createUserEnvironment,
deleteUserEnvironment,
updateUserEnvironment,
} from "./environments.api"
export const environmentsMapper = createMapper()
export const globalEnvironmentMapper = createMapper()
export const storeSyncDefinition: StoreSyncDefinitionOf<
typeof environmentsStore
> = {
async createEnvironment({ name, variables }) {
const lastCreatedEnvIndex = environmentsStore.value.environments.length - 1
const res = await createUserEnvironment(name, JSON.stringify(variables))
if (E.isRight(res)) {
const id = res.right.createUserEnvironment.id
environmentsMapper.addEntry(lastCreatedEnvIndex, id)
}
},
async appendEnvironments({ envs }) {
const appendListLength = envs.length
let appendStart =
environmentsStore.value.environments.length - appendListLength - 1
envs.forEach((env) => {
const envId = ++appendStart
;(async function () {
const res = await createUserEnvironment(
env.name,
JSON.stringify(env.variables)
)
if (E.isRight(res)) {
const id = res.right.createUserEnvironment.id
environmentsMapper.addEntry(envId, id)
}
})()
})
},
async duplicateEnvironment({ envIndex }) {
const environmentToDuplicate = environmentsStore.value.environments.find(
(_, index) => index === envIndex
)
const lastCreatedEnvIndex = environmentsStore.value.environments.length - 1
if (environmentToDuplicate) {
const res = await createUserEnvironment(
environmentToDuplicate?.name,
JSON.stringify(environmentToDuplicate?.variables)
)
if (E.isRight(res)) {
const id = res.right.createUserEnvironment.id
environmentsMapper.addEntry(lastCreatedEnvIndex, id)
}
}
},
updateEnvironment({ envIndex, updatedEnv }) {
const backendId = environmentsMapper.getBackendIdByIndex(envIndex)
console.log(environmentsMapper)
if (backendId) {
updateUserEnvironment(backendId, updatedEnv)()
}
},
async deleteEnvironment({ envIndex }) {
const backendId = environmentsMapper.getBackendIdByIndex(envIndex)
if (backendId) {
await deleteUserEnvironment(backendId)()
environmentsMapper.removeEntry(backendId)
}
},
setGlobalVariables({ entries }) {
const backendId = globalEnvironmentMapper.getBackendIdByIndex(0)
if (backendId) {
updateUserEnvironment(backendId, { name: "", variables: entries })()
}
},
clearGlobalVariables() {
const backendId = globalEnvironmentMapper.getBackendIdByIndex(0)
if (backendId) {
clearGlobalEnvironmentVariables(backendId)
}
},
}
export const environnmentsSyncer = getSyncInitFunction(
environmentsStore,
storeSyncDefinition,
() => settingsStore.value.syncEnvironments,
getSettingSubject("syncEnvironments")
)