ui: refactor the UI with the nav menu (#297)
Co-authored-by: rick <LinuxSuRen@users.noreply.github.com>
This commit is contained in:
parent
40f27c5ba9
commit
02f23fea7a
|
@ -8,6 +8,7 @@ describe('gRPC', () => {
|
|||
it('Create Suite', () => {
|
||||
cy.visit('/')
|
||||
cy.get('.introjs-skipbutton').click()
|
||||
cy.get('[test-id="testing-menu"]').click()
|
||||
|
||||
createTestSuite(store, 'gRPC', suiteName, sampleAPIAddress)
|
||||
})
|
||||
|
|
|
@ -10,6 +10,7 @@ describe('Suite Manage', () => {
|
|||
it('Create Suite', () => {
|
||||
cy.visit('/')
|
||||
cy.get('.introjs-skipbutton').click()
|
||||
cy.get('[test-id="testing-menu"]').click()
|
||||
|
||||
cy.contains('span', 'Tool Box')
|
||||
createTestSuite(store, 'HTTP', suiteName, sampleAPIAddress)
|
||||
|
|
|
@ -1,25 +1,20 @@
|
|||
<script setup lang="ts">
|
||||
import WelcomePage from './views/WelcomePage.vue'
|
||||
import TestCase from './views/TestCase.vue'
|
||||
import TestSuite from './views/TestSuite.vue'
|
||||
import StoreManager from './views/StoreManager.vue'
|
||||
import SecretManager from './views/SecretManager.vue'
|
||||
import TemplateFunctions from './views/TemplateFunctions.vue'
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { ElTree, ElMessage } from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { Edit, Share } from '@element-plus/icons-vue'
|
||||
import type { Suite } from './types'
|
||||
import {
|
||||
Document,
|
||||
Menu as IconMenu,
|
||||
Location,
|
||||
Share,
|
||||
} from '@element-plus/icons-vue'
|
||||
import { ref, watch } from 'vue'
|
||||
import { API } from './views/net'
|
||||
import { Cache } from './views/cache'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import ClientMonitor from 'skywalking-client-js'
|
||||
import { name, version } from '../package'
|
||||
import TestingPanel from './views/TestingPanel.vue'
|
||||
import StoreManager from './views/StoreManager.vue'
|
||||
import SecretManager from './views/SecretManager.vue'
|
||||
import WelcomePage from './views/WelcomePage.vue'
|
||||
|
||||
import setAsDarkTheme from './theme'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const asDarkMode = ref(Cache.GetPreference().darkTheme)
|
||||
setAsDarkTheme(asDarkMode.value)
|
||||
watch(asDarkMode, Cache.WatchDarkTheme)
|
||||
|
@ -27,304 +22,6 @@ watch(asDarkMode, () => {
|
|||
setAsDarkTheme(asDarkMode.value)
|
||||
})
|
||||
|
||||
interface Tree {
|
||||
id: string
|
||||
label: string
|
||||
parent: string
|
||||
parentID: string
|
||||
store: string
|
||||
kind: string
|
||||
children?: Tree[]
|
||||
}
|
||||
|
||||
const testCaseName = ref('')
|
||||
const testSuite = ref('')
|
||||
const testSuiteKind = ref('')
|
||||
const handleNodeClick = (data: Tree) => {
|
||||
if (data.children) {
|
||||
Cache.SetCurrentStore(data.store)
|
||||
viewName.value = 'testsuite'
|
||||
testSuite.value = data.label
|
||||
testSuiteKind.value = data.kind
|
||||
Cache.SetCurrentStore(data.store)
|
||||
|
||||
API.ListTestCase(data.label, data.store, (d) => {
|
||||
if (d.items && d.items.length > 0) {
|
||||
data.children = []
|
||||
d.items.forEach((item: any) => {
|
||||
data.children?.push({
|
||||
id: data.label,
|
||||
label: item.name,
|
||||
kind: data.kind,
|
||||
store: data.store,
|
||||
parent: data.label,
|
||||
parentID: data.id
|
||||
} as Tree)
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Cache.SetCurrentStore(data.store)
|
||||
Cache.SetLastTestCaseLocation(data.parentID, data.id)
|
||||
testCaseName.value = data.label
|
||||
testSuite.value = data.parent
|
||||
testSuiteKind.value = data.kind
|
||||
viewName.value = 'testcase'
|
||||
}
|
||||
}
|
||||
|
||||
const data = ref([] as Tree[])
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>()
|
||||
const currentNodekey = ref('')
|
||||
|
||||
function loadTestSuites(storeName: string) {
|
||||
const requestOptions = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Store-Name': storeName,
|
||||
'X-Auth': API.getToken()
|
||||
},
|
||||
}
|
||||
return async () => {
|
||||
await fetch('/server.Runner/GetSuites', requestOptions)
|
||||
.then((response) => response.json())
|
||||
.then((d) => {
|
||||
if (!d.data) {
|
||||
return
|
||||
}
|
||||
Object.keys(d.data).map((k) => {
|
||||
let suite = {
|
||||
id: k,
|
||||
label: k,
|
||||
kind: d.data[k].kind,
|
||||
store: storeName,
|
||||
children: [] as Tree[]
|
||||
} as Tree
|
||||
|
||||
d.data[k].data.forEach((item: any) => {
|
||||
suite.children?.push({
|
||||
id: k + item,
|
||||
label: item,
|
||||
store: storeName,
|
||||
kind: suite.kind,
|
||||
parent: k,
|
||||
parentID: suite.id
|
||||
} as Tree)
|
||||
})
|
||||
data.value.push(suite)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
interface Store {
|
||||
name: string,
|
||||
description: string,
|
||||
}
|
||||
|
||||
const loginDialogVisible = ref(false)
|
||||
const stores = ref([] as Store[])
|
||||
const storesLoading = ref(false)
|
||||
function loadStores() {
|
||||
storesLoading.value = true
|
||||
const requestOptions = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Auth': API.getToken()
|
||||
}
|
||||
}
|
||||
fetch('/server.Runner/GetStores', requestOptions)
|
||||
.then(API.DefaultResponseProcess)
|
||||
.then(async (d) => {
|
||||
stores.value = d.data
|
||||
data.value = [] as Tree[]
|
||||
Cache.SetStores(d.data)
|
||||
|
||||
for (const item of d.data) {
|
||||
if (item.ready && !item.disabled) {
|
||||
await loadTestSuites(item.name)()
|
||||
}
|
||||
}
|
||||
|
||||
if (data.value.length > 0) {
|
||||
const key = Cache.GetLastTestCaseLocation()
|
||||
|
||||
let targetSuite = {} as Tree
|
||||
let targetChild = {} as Tree
|
||||
if (key.suite !== '' && key.testcase !== '') {
|
||||
for (var i = 0; i < data.value.length; i++) {
|
||||
const item = data.value[i]
|
||||
if (item.id === key.suite && item.children) {
|
||||
for (var j = 0; j < item.children.length; j++) {
|
||||
const child = item.children[j]
|
||||
if (child.id === key.testcase) {
|
||||
targetSuite = item
|
||||
targetChild = child
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetChild.id || targetChild.id === '') {
|
||||
targetSuite = data.value[0]
|
||||
if (targetSuite.children && targetSuite.children.length > 0) {
|
||||
targetChild = targetSuite.children[0]
|
||||
}
|
||||
}
|
||||
|
||||
viewName.value = 'testsuite'
|
||||
currentNodekey.value = targetChild.id
|
||||
treeRef.value!.setCurrentKey(targetChild.id)
|
||||
treeRef.value!.setCheckedKeys([targetChild.id], false)
|
||||
testSuite.value = targetSuite.label
|
||||
Cache.SetCurrentStore(targetSuite.store )
|
||||
testSuiteKind.value = targetChild.kind
|
||||
} else {
|
||||
viewName.value = ""
|
||||
}
|
||||
}).catch((e) => {
|
||||
if(e.message === "Unauthenticated") {
|
||||
loginDialogVisible.value = true
|
||||
} else {
|
||||
ElMessage.error('Oops, ' + e)
|
||||
}
|
||||
}).finally(() => {
|
||||
storesLoading.value = false
|
||||
})
|
||||
}
|
||||
loadStores()
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const importDialogVisible = ref(false)
|
||||
const suiteCreatingLoading = ref(false)
|
||||
const suiteFormRef = ref<FormInstance>()
|
||||
const testSuiteForm = reactive({
|
||||
name: '',
|
||||
api: '',
|
||||
store: '',
|
||||
kind: ''
|
||||
})
|
||||
const importSuiteFormRef = ref<FormInstance>()
|
||||
const importSuiteForm = reactive({
|
||||
url: '',
|
||||
store: ''
|
||||
})
|
||||
|
||||
function openTestSuiteCreateDialog() {
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function openTestSuiteImportDialog() {
|
||||
importDialogVisible.value = true
|
||||
}
|
||||
|
||||
const rules = reactive<FormRules<Suite>>({
|
||||
name: [{ required: true, message: 'Name is required', trigger: 'blur' }],
|
||||
store: [{ required: true, message: 'Location is required', trigger: 'blur' }]
|
||||
})
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid: boolean, fields) => {
|
||||
if (valid) {
|
||||
suiteCreatingLoading.value = true
|
||||
|
||||
API.CreateTestSuite(testSuiteForm, (e) => {
|
||||
suiteCreatingLoading.value = false
|
||||
if (e.error !== "") {
|
||||
ElMessage.error('Oops, ' + e.error)
|
||||
} else {
|
||||
loadStores()
|
||||
dialogVisible.value = false
|
||||
formEl.resetFields()
|
||||
}
|
||||
}, (e) => {
|
||||
suiteCreatingLoading.value = false
|
||||
ElMessage.error('Oops, ' + e)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const importSuiteFormRules = reactive<FormRules<Suite>>({
|
||||
url: [
|
||||
{ required: true, message: 'URL is required', trigger: 'blur' },
|
||||
{ type: 'url', message: 'Should be a valid URL value', trigger: 'blur' }
|
||||
],
|
||||
store: [{ required: true, message: 'Location is required', trigger: 'blur' }]
|
||||
})
|
||||
const importSuiteFormSubmit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid: boolean, fields) => {
|
||||
if (valid) {
|
||||
suiteCreatingLoading.value = true
|
||||
|
||||
API.ImportTestSuite(importSuiteForm, () => {
|
||||
loadStores()
|
||||
importDialogVisible.value = false
|
||||
formEl.resetFields()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const filterText = ref('')
|
||||
watch(filterText, (val) => {
|
||||
treeRef.value!.filter(val)
|
||||
})
|
||||
const filterTestCases = (value: string, data: Tree) => {
|
||||
if (!value) return true
|
||||
return data.label.includes(value)
|
||||
}
|
||||
|
||||
const viewName = ref('')
|
||||
watch(viewName, (val) => {
|
||||
ClientMonitor.setPerformance({
|
||||
service: name,
|
||||
serviceVersion: version,
|
||||
pagePath: val,
|
||||
useFmp: true,
|
||||
enableSPA: true,
|
||||
customTags: [{
|
||||
key: 'theme', value: asDarkMode.value ? 'dark' : 'light'
|
||||
}, {
|
||||
key: 'store', value: Cache.GetCurrentStore().name
|
||||
}]
|
||||
});
|
||||
})
|
||||
|
||||
const deviceAuthActive = ref(0)
|
||||
const deviceAuthResponse = ref({
|
||||
user_code: '',
|
||||
verification_uri: '',
|
||||
device_code: ''
|
||||
})
|
||||
const deviceAuthNext = () => {
|
||||
if (deviceAuthActive.value++ > 2) {
|
||||
return
|
||||
}
|
||||
|
||||
if (deviceAuthActive.value === 1) {
|
||||
fetch('/oauth2/getLocalCode')
|
||||
.then(API.DefaultResponseProcess)
|
||||
.then((d) => {
|
||||
deviceAuthResponse.value = d
|
||||
})
|
||||
} else if (deviceAuthActive.value === 2) {
|
||||
window.location.href = '/oauth2/getUserInfoFromLocalCode?device_code=' + deviceAuthResponse.value.device_code
|
||||
}
|
||||
}
|
||||
|
||||
const suiteKinds = [{
|
||||
"name": "HTTP",
|
||||
}, {
|
||||
"name": "gRPC",
|
||||
}, {
|
||||
"name": "tRPC",
|
||||
}]
|
||||
|
||||
const appVersion = ref('')
|
||||
const appVersionLink = ref('https://github.com/LinuxSuRen/api-testing')
|
||||
API.GetVersion((d) => {
|
||||
|
@ -342,272 +39,64 @@ API.GetVersion((d) => {
|
|||
appVersionLink.value = appVersionLink.value + '/releases/tag/' + version[0]
|
||||
}
|
||||
})
|
||||
|
||||
const panelName = ref('')
|
||||
const sideWidth = ref("width: 300px")
|
||||
const isCollapse = ref(false)
|
||||
watch(isCollapse, (e) => {
|
||||
console.log(e)
|
||||
if (e) {
|
||||
sideWidth.value = "width: 80px"
|
||||
} else {
|
||||
sideWidth.value = "width: 300px"
|
||||
}
|
||||
})
|
||||
const handleSelect = (key: string) => {
|
||||
panelName.value = key
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="common-layout" data-title="Welcome!" data-intro="Welcome to use api-testing! 👋">
|
||||
<el-container style="height: 100%">
|
||||
<el-header style="height: 30px;justify-content: flex-end;">
|
||||
<el-button type="primary" :icon="Edit" @click="viewName = 'secret'" data-intro="Manage the secrets."/>
|
||||
<el-button type="primary" :icon="Share" @click="viewName = 'store'" data-intro="Manage the store backends." />
|
||||
<el-form-item label="Dark Mode" style="margin-left:20px;">
|
||||
<el-switch type="primary" data-intro="Switch light and dark modes" v-model="asDarkMode" />
|
||||
</el-form-item>
|
||||
</el-header>
|
||||
<el-container style="height: 100%">
|
||||
<el-aside :style="sideWidth">
|
||||
<el-radio-group v-model="isCollapse" style="margin-bottom: 20px">
|
||||
<el-radio-button :label="false">+</el-radio-button>
|
||||
<el-radio-button :label="true">-</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-menu
|
||||
class="el-menu-vertical-demo"
|
||||
default-active="welcome"
|
||||
:collapse="isCollapse"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<el-menu-item index="welcome">
|
||||
<el-icon><share /></el-icon>
|
||||
<template #title>Welcome</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="testing" test-id="testing-menu">
|
||||
<el-icon><icon-menu /></el-icon>
|
||||
<template #title>Testing</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="secret">
|
||||
<el-icon><document /></el-icon>
|
||||
<template #title>Secrets</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="store">
|
||||
<el-icon><location /></el-icon>
|
||||
<template #title>Stores</template>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
|
||||
<el-main>
|
||||
<el-container style="height: 100%">
|
||||
<el-aside>
|
||||
<el-button type="primary" @click="openTestSuiteCreateDialog"
|
||||
data-intro="Click here to create a new test suite"
|
||||
test-id="open-new-suite-dialog" :icon="Edit">{{ t('button.new') }}</el-button>
|
||||
<el-button type="primary" @click="openTestSuiteImportDialog"
|
||||
data-intro="Click here to import from Postman"
|
||||
test-id="open-import-suite-dialog">{{ t('button.import') }}</el-button>
|
||||
<el-input v-model="filterText" placeholder="Filter keyword" test-id="search" />
|
||||
<el-main>
|
||||
<TestingPanel v-if="panelName === 'testing'" />
|
||||
<StoreManager v-else-if="panelName === 'store'" />
|
||||
<SecretManager v-else-if="panelName === 'secret'" />
|
||||
<WelcomePage v-else />
|
||||
</el-main>
|
||||
|
||||
<el-tree
|
||||
v-loading="storesLoading"
|
||||
:data=data
|
||||
highlight-current
|
||||
:check-on-click-node="true"
|
||||
:expand-on-click-node="false"
|
||||
:current-node-key="currentNodekey"
|
||||
ref="treeRef"
|
||||
node-key="id"
|
||||
:filter-node-method="filterTestCases"
|
||||
@node-click="handleNodeClick"
|
||||
data-intro="This is the test suite tree. You can click the test suite to edit it."
|
||||
/>
|
||||
</el-aside>
|
||||
|
||||
<el-main>
|
||||
<WelcomePage
|
||||
v-if="viewName === ''"
|
||||
/>
|
||||
<TestCase
|
||||
v-else-if="viewName === 'testcase'"
|
||||
:suite="testSuite"
|
||||
:kindName="testSuiteKind"
|
||||
:name="testCaseName"
|
||||
@updated="loadStores"
|
||||
data-intro="This is the test case editor. You can edit the test case here."
|
||||
/>
|
||||
<TestSuite
|
||||
v-else-if="viewName === 'testsuite'"
|
||||
:name="testSuite"
|
||||
@updated="loadStores"
|
||||
data-intro="This is the test suite editor. You can edit the test suite here."
|
||||
/>
|
||||
<StoreManager
|
||||
v-else-if="viewName === 'store'"
|
||||
/>
|
||||
<SecretManager
|
||||
v-else-if="viewName === 'secret'"
|
||||
/>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-main>
|
||||
<div style="position: absolute; bottom: 0px; right: 10px;">
|
||||
<a :href=appVersionLink target="_blank" rel="noopener">{{appVersion}}</a>
|
||||
</div>
|
||||
<TemplateFunctions/>
|
||||
</el-container>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="dialogVisible" :title="t('title.createTestSuite')" width="30%" draggable>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-form
|
||||
:rules="rules"
|
||||
:model="testSuiteForm"
|
||||
ref="suiteFormRef"
|
||||
status-icon label-width="120px">
|
||||
<el-form-item :label="t('field.storageLocation')" prop="store">
|
||||
<el-select v-model="testSuiteForm.store" class="m-2"
|
||||
test-id="suite-form-store"
|
||||
filterable=true
|
||||
default-first-option=true
|
||||
placeholder="Storage Location" size="middle">
|
||||
<el-option
|
||||
v-for="item in stores"
|
||||
:key="item.name"
|
||||
:label="item.name"
|
||||
:value="item.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('field.suiteKind')" prop="kind">
|
||||
<el-select v-model="testSuiteForm.kind" class="m-2"
|
||||
filterable=true
|
||||
test-id="suite-form-kind"
|
||||
default-first-option=true
|
||||
size="middle">
|
||||
<el-option
|
||||
v-for="item in suiteKinds"
|
||||
:key="item.name"
|
||||
:label="item.name"
|
||||
:value="item.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('field.name')" prop="name">
|
||||
<el-input v-model="testSuiteForm.name" test-id="suite-form-name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="API" prop="api">
|
||||
<el-input v-model="testSuiteForm.api" placeholder="http://foo" test-id="suite-form-api" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="submitForm(suiteFormRef)"
|
||||
:loading="suiteCreatingLoading"
|
||||
test-id="suite-form-submit"
|
||||
>{{ t('button.submit') }}</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="importDialogVisible" title="Import Test Suite" width="30%" draggable>
|
||||
<span>Supported source URL: Postman collection share link</span>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-form
|
||||
:rules="importSuiteFormRules"
|
||||
:model="importSuiteForm"
|
||||
ref="importSuiteFormRef"
|
||||
status-icon label-width="120px">
|
||||
<el-form-item label="Location" prop="store">
|
||||
<el-select v-model="importSuiteForm.store" class="m-2"
|
||||
test-id="suite-import-form-store"
|
||||
filterable=true
|
||||
default-first-option=true
|
||||
placeholder="Storage Location" size="middle">
|
||||
<el-option
|
||||
v-for="item in stores"
|
||||
:key="item.name"
|
||||
:label="item.name"
|
||||
:value="item.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="URL" prop="url">
|
||||
<el-input v-model="importSuiteForm.url" test-id="suite-import-form-api" placeholder="https://api.postman.com/collections/xxx" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="importSuiteFormSubmit(importSuiteFormRef)"
|
||||
test-id="suite-import-submit"
|
||||
>{{ t('button.import') }}</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="loginDialogVisible"
|
||||
title="You need to login first."
|
||||
width="30%"
|
||||
>
|
||||
<el-collapse accordion="true">
|
||||
<el-collapse-item title="Server in cloud" name="1">
|
||||
<a href="/oauth2/token" target="_blank">
|
||||
<svg height="32" aria-hidden="true" viewBox="0 0 16 16" version="1.1" width="32" data-view-component="true" class="octicon octicon-mark-github v-align-middle color-fg-default">
|
||||
<path d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"></path>
|
||||
</svg>
|
||||
</a>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="Server in local" name="2">
|
||||
<el-steps :active="deviceAuthActive" finish-status="success">
|
||||
<el-step title="Request Device Code" />
|
||||
<el-step title="Input Code"/>
|
||||
<el-step title="Finished" />
|
||||
</el-steps>
|
||||
|
||||
<div v-if="deviceAuthActive===1">
|
||||
Open <a :href="deviceAuthResponse.verification_uri" target="_blank">this link</a>, and type the code: <span>{{ deviceAuthResponse.user_code }}. Then click the next step button.</span>
|
||||
</div>
|
||||
<el-button style="margin-top: 12px" @click="deviceAuthNext">Next step</el-button>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</el-dialog>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
header {
|
||||
line-height: 1.5;
|
||||
max-height: 100vh;
|
||||
}
|
||||
.common-layout {
|
||||
height: 100%;
|
||||
}
|
||||
.logo {
|
||||
display: block;
|
||||
margin: 0 auto 2rem;
|
||||
}
|
||||
|
||||
nav {
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
|
||||
nav a.router-link-exact-active {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
nav a.router-link-exact-active:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
nav a {
|
||||
display: inline-block;
|
||||
padding: 0 1rem;
|
||||
border-left: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
nav a:first-of-type {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
header {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
padding-right: calc(var(--section-gap) / 2);
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 0 2rem 0 0;
|
||||
}
|
||||
|
||||
header .wrapper {
|
||||
display: flex;
|
||||
place-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
nav {
|
||||
text-align: left;
|
||||
margin-left: -1rem;
|
||||
font-size: 1rem;
|
||||
|
||||
padding: 1rem 0;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
.demo-tabs > .el-tabs__content {
|
||||
padding: 32px;
|
||||
color: #6b778c;
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,549 @@
|
|||
<script setup lang="ts">
|
||||
import WelcomePage from './WelcomePage.vue'
|
||||
import TestCase from './TestCase.vue'
|
||||
import TestSuite from './TestSuite.vue'
|
||||
import TemplateFunctions from './TemplateFunctions.vue'
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { ElTree, ElMessage } from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { Edit } from '@element-plus/icons-vue'
|
||||
import type { Suite } from './types'
|
||||
import { API } from './net'
|
||||
import { Cache } from './cache'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
interface Tree {
|
||||
id: string
|
||||
label: string
|
||||
parent: string
|
||||
parentID: string
|
||||
store: string
|
||||
kind: string
|
||||
children?: Tree[]
|
||||
}
|
||||
|
||||
const testCaseName = ref('')
|
||||
const testSuite = ref('')
|
||||
const testSuiteKind = ref('')
|
||||
const handleNodeClick = (data: Tree) => {
|
||||
if (data.children) {
|
||||
Cache.SetCurrentStore(data.store)
|
||||
viewName.value = 'testsuite'
|
||||
testSuite.value = data.label
|
||||
testSuiteKind.value = data.kind
|
||||
Cache.SetCurrentStore(data.store)
|
||||
|
||||
API.ListTestCase(data.label, data.store, (d) => {
|
||||
if (d.items && d.items.length > 0) {
|
||||
data.children = []
|
||||
d.items.forEach((item: any) => {
|
||||
data.children?.push({
|
||||
id: data.label,
|
||||
label: item.name,
|
||||
kind: data.kind,
|
||||
store: data.store,
|
||||
parent: data.label,
|
||||
parentID: data.id
|
||||
} as Tree)
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Cache.SetCurrentStore(data.store)
|
||||
Cache.SetLastTestCaseLocation(data.parentID, data.id)
|
||||
testCaseName.value = data.label
|
||||
testSuite.value = data.parent
|
||||
testSuiteKind.value = data.kind
|
||||
viewName.value = 'testcase'
|
||||
}
|
||||
}
|
||||
|
||||
const data = ref([] as Tree[])
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>()
|
||||
const currentNodekey = ref('')
|
||||
|
||||
function loadTestSuites(storeName: string) {
|
||||
const requestOptions = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Store-Name': storeName,
|
||||
'X-Auth': API.getToken()
|
||||
},
|
||||
}
|
||||
return async () => {
|
||||
await fetch('/server.Runner/GetSuites', requestOptions)
|
||||
.then((response) => response.json())
|
||||
.then((d) => {
|
||||
if (!d.data) {
|
||||
return
|
||||
}
|
||||
Object.keys(d.data).map((k) => {
|
||||
let suite = {
|
||||
id: k,
|
||||
label: k,
|
||||
kind: d.data[k].kind,
|
||||
store: storeName,
|
||||
children: [] as Tree[]
|
||||
} as Tree
|
||||
|
||||
d.data[k].data.forEach((item: any) => {
|
||||
suite.children?.push({
|
||||
id: k + item,
|
||||
label: item,
|
||||
store: storeName,
|
||||
kind: suite.kind,
|
||||
parent: k,
|
||||
parentID: suite.id
|
||||
} as Tree)
|
||||
})
|
||||
data.value.push(suite)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
interface Store {
|
||||
name: string,
|
||||
description: string,
|
||||
}
|
||||
|
||||
const loginDialogVisible = ref(false)
|
||||
const stores = ref([] as Store[])
|
||||
const storesLoading = ref(false)
|
||||
function loadStores() {
|
||||
storesLoading.value = true
|
||||
const requestOptions = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Auth': API.getToken()
|
||||
}
|
||||
}
|
||||
fetch('/server.Runner/GetStores', requestOptions)
|
||||
.then(API.DefaultResponseProcess)
|
||||
.then(async (d) => {
|
||||
stores.value = d.data
|
||||
data.value = [] as Tree[]
|
||||
Cache.SetStores(d.data)
|
||||
|
||||
for (const item of d.data) {
|
||||
if (item.ready && !item.disabled) {
|
||||
await loadTestSuites(item.name)()
|
||||
}
|
||||
}
|
||||
|
||||
if (data.value.length > 0) {
|
||||
const key = Cache.GetLastTestCaseLocation()
|
||||
|
||||
let targetSuite = {} as Tree
|
||||
let targetChild = {} as Tree
|
||||
if (key.suite !== '' && key.testcase !== '') {
|
||||
for (var i = 0; i < data.value.length; i++) {
|
||||
const item = data.value[i]
|
||||
if (item.id === key.suite && item.children) {
|
||||
for (var j = 0; j < item.children.length; j++) {
|
||||
const child = item.children[j]
|
||||
if (child.id === key.testcase) {
|
||||
targetSuite = item
|
||||
targetChild = child
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetChild.id || targetChild.id === '') {
|
||||
targetSuite = data.value[0]
|
||||
if (targetSuite.children && targetSuite.children.length > 0) {
|
||||
targetChild = targetSuite.children[0]
|
||||
}
|
||||
}
|
||||
|
||||
viewName.value = 'testsuite'
|
||||
currentNodekey.value = targetChild.id
|
||||
treeRef.value!.setCurrentKey(targetChild.id)
|
||||
treeRef.value!.setCheckedKeys([targetChild.id], false)
|
||||
testSuite.value = targetSuite.label
|
||||
Cache.SetCurrentStore(targetSuite.store )
|
||||
testSuiteKind.value = targetChild.kind
|
||||
} else {
|
||||
viewName.value = ""
|
||||
}
|
||||
}).catch((e) => {
|
||||
if(e.message === "Unauthenticated") {
|
||||
loginDialogVisible.value = true
|
||||
} else {
|
||||
ElMessage.error('Oops, ' + e)
|
||||
}
|
||||
}).finally(() => {
|
||||
storesLoading.value = false
|
||||
})
|
||||
}
|
||||
loadStores()
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const importDialogVisible = ref(false)
|
||||
const suiteCreatingLoading = ref(false)
|
||||
const suiteFormRef = ref<FormInstance>()
|
||||
const testSuiteForm = reactive({
|
||||
name: '',
|
||||
api: '',
|
||||
store: '',
|
||||
kind: ''
|
||||
})
|
||||
const importSuiteFormRef = ref<FormInstance>()
|
||||
const importSuiteForm = reactive({
|
||||
url: '',
|
||||
store: ''
|
||||
})
|
||||
|
||||
function openTestSuiteCreateDialog() {
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function openTestSuiteImportDialog() {
|
||||
importDialogVisible.value = true
|
||||
}
|
||||
|
||||
const rules = reactive<FormRules<Suite>>({
|
||||
name: [{ required: true, message: 'Name is required', trigger: 'blur' }],
|
||||
store: [{ required: true, message: 'Location is required', trigger: 'blur' }]
|
||||
})
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
suiteCreatingLoading.value = true
|
||||
|
||||
API.CreateTestSuite(testSuiteForm, (e) => {
|
||||
suiteCreatingLoading.value = false
|
||||
if (e.error !== "") {
|
||||
ElMessage.error('Oops, ' + e.error)
|
||||
} else {
|
||||
loadStores()
|
||||
dialogVisible.value = false
|
||||
formEl.resetFields()
|
||||
}
|
||||
}, (e) => {
|
||||
suiteCreatingLoading.value = false
|
||||
ElMessage.error('Oops, ' + e)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const importSuiteFormRules = reactive<FormRules<Suite>>({
|
||||
url: [
|
||||
{ required: true, message: 'URL is required', trigger: 'blur' },
|
||||
{ type: 'url', message: 'Should be a valid URL value', trigger: 'blur' }
|
||||
],
|
||||
store: [{ required: true, message: 'Location is required', trigger: 'blur' }]
|
||||
})
|
||||
const importSuiteFormSubmit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
suiteCreatingLoading.value = true
|
||||
|
||||
API.ImportTestSuite(importSuiteForm, () => {
|
||||
loadStores()
|
||||
importDialogVisible.value = false
|
||||
formEl.resetFields()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const filterText = ref('')
|
||||
watch(filterText, (val) => {
|
||||
treeRef.value!.filter(val)
|
||||
})
|
||||
const filterTestCases = (value: string, data: Tree) => {
|
||||
if (!value) return true
|
||||
return data.label.includes(value)
|
||||
}
|
||||
|
||||
const viewName = ref('')
|
||||
|
||||
const deviceAuthActive = ref(0)
|
||||
const deviceAuthResponse = ref({
|
||||
user_code: '',
|
||||
verification_uri: '',
|
||||
device_code: ''
|
||||
})
|
||||
const deviceAuthNext = () => {
|
||||
if (deviceAuthActive.value++ > 2) {
|
||||
return
|
||||
}
|
||||
|
||||
if (deviceAuthActive.value === 1) {
|
||||
fetch('/oauth2/getLocalCode')
|
||||
.then(API.DefaultResponseProcess)
|
||||
.then((d) => {
|
||||
deviceAuthResponse.value = d
|
||||
})
|
||||
} else if (deviceAuthActive.value === 2) {
|
||||
window.location.href = '/oauth2/getUserInfoFromLocalCode?device_code=' + deviceAuthResponse.value.device_code
|
||||
}
|
||||
}
|
||||
|
||||
const suiteKinds = [{
|
||||
"name": "HTTP",
|
||||
}, {
|
||||
"name": "gRPC",
|
||||
}, {
|
||||
"name": "tRPC",
|
||||
}]
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="common-layout" data-title="Welcome!" data-intro="Welcome to use api-testing! 👋">
|
||||
<el-container style="height: 100%">
|
||||
<el-main>
|
||||
<el-container style="height: 100%">
|
||||
<el-aside>
|
||||
<el-button type="primary" @click="openTestSuiteCreateDialog"
|
||||
data-intro="Click here to create a new test suite"
|
||||
test-id="open-new-suite-dialog" :icon="Edit">{{ t('button.new') }}</el-button>
|
||||
<el-button type="primary" @click="openTestSuiteImportDialog"
|
||||
data-intro="Click here to import from Postman"
|
||||
test-id="open-import-suite-dialog">{{ t('button.import') }}</el-button>
|
||||
<el-input v-model="filterText" placeholder="Filter keyword" test-id="search" />
|
||||
|
||||
<el-tree
|
||||
v-loading="storesLoading"
|
||||
:data=data
|
||||
highlight-current
|
||||
:check-on-click-node="true"
|
||||
:expand-on-click-node="false"
|
||||
:current-node-key="currentNodekey"
|
||||
ref="treeRef"
|
||||
node-key="id"
|
||||
:filter-node-method="filterTestCases"
|
||||
@node-click="handleNodeClick"
|
||||
data-intro="This is the test suite tree. You can click the test suite to edit it."
|
||||
/>
|
||||
</el-aside>
|
||||
|
||||
<el-main>
|
||||
<TestCase
|
||||
v-if="viewName === 'testcase'"
|
||||
:suite="testSuite"
|
||||
:kindName="testSuiteKind"
|
||||
:name="testCaseName"
|
||||
@updated="loadStores"
|
||||
data-intro="This is the test case editor. You can edit the test case here."
|
||||
/>
|
||||
<TestSuite
|
||||
v-else-if="viewName === 'testsuite'"
|
||||
:name="testSuite"
|
||||
@updated="loadStores"
|
||||
data-intro="This is the test suite editor. You can edit the test suite here."
|
||||
/>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-main>
|
||||
|
||||
<TemplateFunctions/>
|
||||
</el-container>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="dialogVisible" :title="t('title.createTestSuite')" width="30%" draggable>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-form
|
||||
:rules="rules"
|
||||
:model="testSuiteForm"
|
||||
ref="suiteFormRef"
|
||||
status-icon label-width="120px">
|
||||
<el-form-item :label="t('field.storageLocation')" prop="store">
|
||||
<el-select v-model="testSuiteForm.store" class="m-2"
|
||||
test-id="suite-form-store"
|
||||
filterable=true
|
||||
default-first-option=true
|
||||
placeholder="Storage Location" size="middle">
|
||||
<el-option
|
||||
v-for="item in stores"
|
||||
:key="item.name"
|
||||
:label="item.name"
|
||||
:value="item.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('field.suiteKind')" prop="kind">
|
||||
<el-select v-model="testSuiteForm.kind" class="m-2"
|
||||
filterable=true
|
||||
test-id="suite-form-kind"
|
||||
default-first-option=true
|
||||
size="middle">
|
||||
<el-option
|
||||
v-for="item in suiteKinds"
|
||||
:key="item.name"
|
||||
:label="item.name"
|
||||
:value="item.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('field.name')" prop="name">
|
||||
<el-input v-model="testSuiteForm.name" test-id="suite-form-name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="API" prop="api">
|
||||
<el-input v-model="testSuiteForm.api" placeholder="http://foo" test-id="suite-form-api" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="submitForm(suiteFormRef)"
|
||||
:loading="suiteCreatingLoading"
|
||||
test-id="suite-form-submit"
|
||||
>{{ t('button.submit') }}</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="importDialogVisible" title="Import Test Suite" width="30%" draggable>
|
||||
<span>Supported source URL: Postman collection share link</span>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-form
|
||||
:rules="importSuiteFormRules"
|
||||
:model="importSuiteForm"
|
||||
ref="importSuiteFormRef"
|
||||
status-icon label-width="120px">
|
||||
<el-form-item label="Location" prop="store">
|
||||
<el-select v-model="importSuiteForm.store" class="m-2"
|
||||
test-id="suite-import-form-store"
|
||||
filterable=true
|
||||
default-first-option=true
|
||||
placeholder="Storage Location" size="middle">
|
||||
<el-option
|
||||
v-for="item in stores"
|
||||
:key="item.name"
|
||||
:label="item.name"
|
||||
:value="item.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="URL" prop="url">
|
||||
<el-input v-model="importSuiteForm.url" test-id="suite-import-form-api" placeholder="https://api.postman.com/collections/xxx" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="importSuiteFormSubmit(importSuiteFormRef)"
|
||||
test-id="suite-import-submit"
|
||||
>{{ t('button.import') }}</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="loginDialogVisible"
|
||||
title="You need to login first."
|
||||
width="30%"
|
||||
>
|
||||
<el-collapse accordion="true">
|
||||
<el-collapse-item title="Server in cloud" name="1">
|
||||
<a href="/oauth2/token" target="_blank">
|
||||
<svg height="32" aria-hidden="true" viewBox="0 0 16 16" version="1.1" width="32" data-view-component="true" class="octicon octicon-mark-github v-align-middle color-fg-default">
|
||||
<path d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"></path>
|
||||
</svg>
|
||||
</a>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="Server in local" name="2">
|
||||
<el-steps :active="deviceAuthActive" finish-status="success">
|
||||
<el-step title="Request Device Code" />
|
||||
<el-step title="Input Code"/>
|
||||
<el-step title="Finished" />
|
||||
</el-steps>
|
||||
|
||||
<div v-if="deviceAuthActive===1">
|
||||
Open <a :href="deviceAuthResponse.verification_uri" target="_blank">this link</a>, and type the code: <span>{{ deviceAuthResponse.user_code }}. Then click the next step button.</span>
|
||||
</div>
|
||||
<el-button style="margin-top: 12px" @click="deviceAuthNext">Next step</el-button>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
header {
|
||||
line-height: 1.5;
|
||||
max-height: 100vh;
|
||||
}
|
||||
.common-layout {
|
||||
height: 100%;
|
||||
}
|
||||
.logo {
|
||||
display: block;
|
||||
margin: 0 auto 2rem;
|
||||
}
|
||||
|
||||
nav {
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
nav a.router-link-exact-active {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
nav a.router-link-exact-active:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
nav a {
|
||||
display: inline-block;
|
||||
padding: 0 1rem;
|
||||
border-left: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
nav a:first-of-type {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
header {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
padding-right: calc(var(--section-gap) / 2);
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 0 2rem 0 0;
|
||||
}
|
||||
|
||||
header .wrapper {
|
||||
display: flex;
|
||||
place-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
nav {
|
||||
text-align: left;
|
||||
margin-left: -1rem;
|
||||
font-size: 1rem;
|
||||
|
||||
padding: 1rem 0;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
.demo-tabs > .el-tabs__content {
|
||||
padding: 32px;
|
||||
color: #6b778c;
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
30
go.work.sum
30
go.work.sum
|
@ -251,13 +251,17 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUu
|
|||
gioui.org v0.0.0-20210308172011-57750fc8a0a6 h1:K72hopUosKG3ntOPNG4OzzbuhxGuVf06fa2la1/H/Ho=
|
||||
git.sr.ht/~sbinet/gg v0.3.1 h1:LNhjNn8DerC8f9DHLz6lS0YYul/b602DUxDgGkd/Aik=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
|
||||
github.com/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU=
|
||||
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
|
||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=
|
||||
github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9 h1:7kQgkwGRoLzC9K0oyXdJo7nve/bynv/KwUsxbiTlzAM=
|
||||
github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19 h1:iXUgAaqDcIUGbRoy2TdeofRG/j1zpGRSEmNK05T+bi8=
|
||||
|
@ -280,13 +284,16 @@ github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8
|
|||
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
|
||||
github.com/bwesterb/go-ristretto v1.2.3 h1:1w53tCkGhCQ5djbat3+MH0BAQ5Kfgbt56UZQ/JMzngw=
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
|
||||
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
||||
github.com/chzyer/readline v1.5.0 h1:lSwwFrbNviGePhkewF1az4oLmcwqCZijQ2/Wi3BGHAI=
|
||||
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
|
||||
|
@ -310,10 +317,12 @@ github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byA
|
|||
github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
|
||||
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c=
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
|
||||
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
||||
github.com/go-fonts/dejavu v0.1.0 h1:JSajPXURYqpr+Cu8U9bt8K+XcACIHWqWrvWCKyeFmVQ=
|
||||
|
@ -329,16 +338,19 @@ github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBj
|
|||
github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81 h1:6zl3BbBhdnMkpSj2YY30qV3gDcVBGtFgVsV3+/i+mKQ=
|
||||
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-pdf/fpdf v0.6.0 h1:MlgtGIfsdMEEQJr2le6b/HNr1ZlQwxyWr77r2aj2U/8=
|
||||
github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
|
||||
github.com/go-session/session v3.1.2+incompatible h1:yStchEObKg4nk2F7JGE7KoFIrA/1Y078peagMWcrncg=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
|
||||
github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
|
||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
github.com/google/cel-go v0.12.5 h1:DmzaiSgoaqGCjtpPQWl26/gND+yRpim56H1jCVev6d8=
|
||||
github.com/google/cel-go v0.12.5/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
|
@ -355,9 +367,13 @@ github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5i
|
|||
github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 h1:tlyzajkF3030q6M8SvmJSemC9DTHL/xaMa18b65+JM4=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
|
||||
github.com/grpc/grpc-go v1.55.0 h1:8UgTz8i19QobJ8MwLklNLqXDoMHRBGS0ZI7h2OrVqYc=
|
||||
github.com/grpc/grpc-go v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
|
||||
|
@ -377,6 +393,8 @@ github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR3
|
|||
github.com/hashicorp/serf v0.9.7 h1:hkdgbqizGQHuU5IPqYM1JdSMV8nKfpuOnZYXssk9muY=
|
||||
github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
|
||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
github.com/jhump/gopoet v0.0.0-20190322174617-17282ff210b3/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI=
|
||||
|
@ -386,6 +404,7 @@ github.com/jhump/goprotoc v0.5.0 h1:Y1UgUX+txUznfqcGdDef8ZOVlyQvnV0pKWZH08RmZuo=
|
|||
github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7UvLHRQ=
|
||||
github.com/jhump/protoreflect v1.11.0/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E=
|
||||
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
|
||||
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
||||
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
|
||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||
|
@ -409,6 +428,7 @@ github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYt
|
|||
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
|
@ -416,14 +436,17 @@ github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLv
|
|||
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs=
|
||||
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
|
||||
github.com/mmcloughlin/avo v0.5.0 h1:nAco9/aI9Lg2kiuROBY6BhCI/z0t5jEvJfjWbL8qXLU=
|
||||
github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM=
|
||||
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
|
||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI=
|
||||
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/nats-io/nats-server/v2 v2.9.15/go.mod h1:QlCTy115fqpx4KSOPFIxSV7DdI6OxtZsGOL1JLdeRlE=
|
||||
github.com/nats-io/nats.go v1.25.0/go.mod h1:D2WALIhz7V8M0pH8Scx8JZXlg6Oqz5VG+nQkK8nJdvg=
|
||||
github.com/onsi/ginkgo v1.13.0 h1:M76yO2HkZASFjXL0HSoZJ1AYEmQxNJmY41Jx1zNUq1Y=
|
||||
|
@ -431,6 +454,7 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
|||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/phpdave11/gofpdf v1.4.2 h1:KPKiIbfwbvC/wOncwhrpRdXVj2CZTCFlw4wnoyjtHfQ=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
|
||||
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
|
||||
|
@ -448,20 +472,24 @@ github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5A
|
|||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
|
||||
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||
github.com/xhit/go-str2duration v1.2.0 h1:BcV5u025cITWxEQKGWr1URRzrcXtu7uk8+luz3Yuhwc=
|
||||
github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1v2SRTV4cUmp4=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
|
||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489 h1:1JFLBqwIgdyHN1ZtgjTBwO+blA6gVOmZurpiMEsETKo=
|
||||
go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8=
|
||||
|
@ -539,7 +567,9 @@ google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQf
|
|||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||
gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
|
||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||
honnef.co/go/tools v0.1.3 h1:qTakTkI6ni6LFD5sBwwsdSO+AQqbSIxOauHTTQKZ/7o=
|
||||
k8s.io/apiserver v0.26.0 h1:q+LqIK5EZwdznGZb8bq0+a+vCqdeEEe4Ux3zsOjbc4o=
|
||||
k8s.io/apiserver v0.26.0/go.mod h1:aWhlLD+mU+xRo+zhkvP/gFNbShI4wBDHS33o0+JGI84=
|
||||
|
|
Loading…
Reference in New Issue