feat: support set a store as readonly (#269)

This commit is contained in:
Rick 2023-11-12 07:31:15 +08:00 committed by GitHub
parent b8bf7ffcbe
commit af92ea623d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 1076 additions and 382 deletions

View File

@ -111,7 +111,11 @@ jobs:
- name: Core Image
run: GOPROXY=direct IMG_TOOL=docker make build-image
- name: Run e2e
env:
GITEE_TOKEN: ${{ secrets.GITEE_TOKEN }}
run: |
sudo curl -L https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
sudo chmod u+x /usr/local/bin/docker-compose
make test-e2e
- name: Operator Image
run: cd operator && make docker-build
@ -136,4 +140,4 @@ jobs:
sudo atest service install
sudo atest service restart
- name: Test
run: make test-ui test-e2e
run: make test-ui

View File

@ -1,3 +1,8 @@
# See https://www.gitpod.io/docs/configure/workspaces
tasks:
- init: make build
command: docker run --pull always --network host ghcr.io/linuxsuren/api-testing:master
- init: make init-env
before: IMG_TOOL=docker GOPROXY= make build-ext copy-ext build-image
command: cd console/atest-ui/ && npm i
ports:
- port: 5713 # console interactive port

View File

@ -6,21 +6,27 @@ HELM_VERSION?=v0.0.2
APP_VERSION?=v0.0.13
HELM_REPO?=docker.io/linuxsuren
fmt:
go fmt ./...
cd extensions/store-etcd && go fmt ./...
cd extensions/store-git && go fmt ./...
cd extensions/store-orm && go fmt ./...
cd extensions/store-s3 && go fmt ./...
build:
mkdir -p bin
rm -rf bin/atest
go build ${TOOLEXEC} -a -o bin/atest main.go
build-ext: build-ext-git build-ext-orm build-ext-s3
build-ext: build-ext-git build-ext-orm build-ext-s3 build-ext-etcd
build-ext-git:
CGO_ENABLED=0 go build -ldflags "-w -s" -o bin/atest-store-git extensions/store-git/main.go
build-ext-orm:
CGO_ENABLED=0 go build -ldflags "-w -s" -o bin/atest-store-orm extensions/store-orm/main.go
build-ext-etcd:
CGO_ENABLED=0 go build -ldflags "-w -s" -o bin/atest-store-etcd extensions/store-etcd/main.go
build-ui:
cd console/atest-ui && npm i && npm run build-only
build-ext-s3:
CGO_ENABLED=0 go build -ldflags "-w -s" -o bin/atest-store-s3 extensions/store-s3/main.go
build-ui:
cd console/atest-ui && npm i && npm run build-only
embed-ui:
cd console/atest-ui && npm i && npm run build-only
cp console/atest-ui/dist/index.html cmd/data/index.html
@ -98,7 +104,7 @@ test-operator:
test-all-backend: test test-collector test-store-orm test-store-s3 test-store-git test-store-etcd
test-all: test-all-backend test-ui
test-e2e:
cd extensions/e2e && docker-compose build && docker-compose up && docker-compose down
cd extensions/e2e && ./start.sh
install-precheck:
cp .github/pre-commit .git/hooks/pre-commit
@ -151,3 +157,7 @@ install-tool:
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
hd i protoc-gen-grpc-web
hd i protoc-gen-grpc-gateway
init-env:
curl https://linuxsuren.github.io/tools/install.sh|bash
hd i cli/cli
gh extension install linuxsuren/gh-dev

View File

@ -10,6 +10,7 @@
"dependencies": {
"element-plus": "^2.3.7",
"intro.js": "^7.0.1",
"jsonpath-plus": "^7.2.0",
"skywalking-client-js": "^0.10.0",
"vue": "^3.3.4",
"vue-i18n": "^9.2.2",
@ -8747,6 +8748,14 @@
"graceful-fs": "^4.1.6"
}
},
"node_modules/jsonpath-plus": {
"version": "7.2.0",
"resolved": "https://registry.npmmirror.com/jsonpath-plus/-/jsonpath-plus-7.2.0.tgz",
"integrity": "sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==",
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/jsprim": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz",
@ -18936,6 +18945,11 @@
"universalify": "^2.0.0"
}
},
"jsonpath-plus": {
"version": "7.2.0",
"resolved": "https://registry.npmmirror.com/jsonpath-plus/-/jsonpath-plus-7.2.0.tgz",
"integrity": "sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA=="
},
"jsprim": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz",

View File

@ -18,6 +18,7 @@
"dependencies": {
"element-plus": "^2.3.7",
"intro.js": "^7.0.1",
"jsonpath-plus": "^7.2.0",
"skywalking-client-js": "^0.10.0",
"vue": "^3.3.4",
"vue-i18n": "^9.2.2",

View File

@ -10,7 +10,7 @@ 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 { GetLastTestCaseLocation, SetLastTestCaseLocation } from './views/cache'
import { Cache } from './views/cache'
import { DefaultResponseProcess } from './views/net'
import { useI18n } from 'vue-i18n'
import ClientMonitor from 'skywalking-client-js'
@ -20,11 +20,12 @@ import setAsDarkTheme from './theme'
const { t } = useI18n()
const asDarkMode = ref(false)
function switchAppMode()
{
const asDarkMode = ref(Cache.GetPreference().darkTheme)
setAsDarkTheme(asDarkMode.value)
watch(asDarkMode, Cache.WatchDarkTheme)
watch(asDarkMode, () => {
setAsDarkTheme(asDarkMode.value)
}
})
interface Tree {
id: string
@ -38,14 +39,13 @@ interface Tree {
const testCaseName = ref('')
const testSuite = ref('')
const store = ref('')
const testSuiteKind = ref('')
const handleNodeClick = (data: Tree) => {
if (data.children) {
viewName.value = 'testsuite'
testSuite.value = data.label
store.value = data.store
testSuiteKind.value = data.kind
Cache.SetCurrentStore(data.store)
const requestOptions = {
method: 'POST',
@ -74,10 +74,10 @@ const handleNodeClick = (data: Tree) => {
}
})
} else {
SetLastTestCaseLocation(data.parentID, data.id)
Cache.SetLastTestCaseLocation(data.parentID, data.id)
testCaseName.value = data.label
testSuite.value = data.parent
store.value = data.store
Cache.SetCurrentStore(data.store)
testSuiteKind.value = data.kind
viewName.value = 'testcase'
}
@ -141,15 +141,16 @@ function loadStores() {
.then(async (d) => {
stores.value = d.data
data.value = [] as Tree[]
Cache.SetStores(d.data)
for (const item of d.data) {
if (item.ready) {
if (item.ready && !item.disabled) {
await loadTestSuites(item.name)()
}
}
if (data.value.length > 0) {
const key = GetLastTestCaseLocation()
const key = Cache.GetLastTestCaseLocation()
let targetSuite = {} as Tree
let targetChild = {} as Tree
@ -182,7 +183,7 @@ function loadStores() {
treeRef.value!.setCurrentKey(targetChild.id)
treeRef.value!.setCheckedKeys([targetChild.id], false)
testSuite.value = targetSuite.label
store.value = targetSuite.store
Cache.SetCurrentStore(targetSuite.store )
testSuiteKind.value = targetChild.kind
} else {
viewName.value = ""
@ -311,7 +312,7 @@ watch(viewName, (val) => {
customTags: [{
key: 'theme', value: asDarkMode.value ? 'dark' : 'light'
}, {
key: 'store', value: store.value
key: 'store', value: Cache.GetCurrentStore().name
}]
});
})
@ -332,7 +333,7 @@ const suiteKinds = [{
<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" @click="switchAppMode"/>
<el-switch type="primary" data-intro="Switch light and dark modes" v-model="asDarkMode" />
</el-form-item>
</el-header>
@ -369,7 +370,6 @@ const suiteKinds = [{
/>
<TestCase
v-else-if="viewName === 'testcase'"
:store="store"
:suite="testSuite"
:kindName="testSuiteKind"
:name="testCaseName"
@ -379,7 +379,6 @@ const suiteKinds = [{
<TestSuite
v-else-if="viewName === 'testsuite'"
:name="testSuite"
:store="store"
@updated="loadStores"
data-intro="This is the test suite editor. You can edit the test suite here."
/>

View File

@ -38,6 +38,7 @@
"plugin": "Plugin",
"pluginName": "Plugin Name",
"pluginURL": "Plugin URL",
"disabled": "Disabled",
"status": "Status",
"operations": "Operations",
"storageLocation": "Storage Location",

View File

@ -22,7 +22,9 @@ const emptyStore = function() {
properties: [{
key: '',
value: ''
}]
}],
disabled: false,
readonly: false
} as Store
}
const stores = ref([] as Store[])
@ -38,6 +40,8 @@ interface Store {
username: string
password: string
ready: boolean
disabled: boolean
readonly: boolean
kind: {
name: string
url: string
@ -86,6 +90,7 @@ function editStore(name: string) {
stores.value.forEach((e: Store) => {
if (e.name === name) {
setStoreForm(e)
return
}
})
createAction.value = false
@ -97,7 +102,13 @@ function setStoreForm(store: Store) {
storeForm.username = store.username
storeForm.password = store.password
storeForm.kind = store.kind
storeForm.disabled = store.disabled
storeForm.readonly = store.readonly
storeForm.properties = store.properties
storeForm.properties.push({
key: '',
value: ''
})
}
function addStore() {
@ -107,7 +118,9 @@ function addStore() {
}
const rules = reactive<FormRules<Store>>({
name: [{ required: true, message: 'Name is required', trigger: 'blur' }]
name: [{ required: true, message: 'Name is required', trigger: 'blur' }],
url: [{ required: true, message: 'URL is required', trigger: 'blur' }],
"kind.name": [{ required: true, message: 'Plugin is required', trigger: 'blur' }]
})
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
@ -166,7 +179,7 @@ function storeVerify(formEl: FormInstance | undefined) {
}
})
.then((e) => {
if (e.success) {
if (e.ready) {
ElMessage({
message: 'Verified!',
type: 'success'
@ -284,6 +297,9 @@ function updateKeys() {
<el-form-item :label="t('field.pluginURL')" prop="plugin">
<el-input v-model="storeForm.kind.url" test-id="store-form-plugin" />
</el-form-item>
<el-form-item :label="t('field.disabled')" prop="disabled">
<el-switch v-model="storeForm.disabled" />
</el-form-item>
<el-form-item :label="t('field.properties')" prop="properties">
<el-table :data="storeForm.properties" style="width: 100%">
<el-table-column label="Key" width="180">

View File

@ -1,26 +1,29 @@
<script setup lang="ts">
import { ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { Edit, Delete } from '@element-plus/icons-vue'
import { Edit, Delete, Search } from '@element-plus/icons-vue'
import JsonViewer from 'vue-json-viewer'
import type { Pair, TestResult, TestCaseWithSuite } from './types'
import { NewSuggestedAPIsQuery, CreateFilter, GetHTTPMethods, FlattenObject } from './types'
import { GetTestCaseResponseCache, SetTestCaseResponseCache } from './cache'
import { Cache } from './cache'
import type { TestCaseResponse } from './cache'
import { useI18n } from 'vue-i18n'
import { JSONPath } from 'jsonpath-plus'
const { t } = useI18n()
const props = defineProps({
name: String,
suite: String,
store: String,
kindName: String,
})
const store = Cache.GetCurrentStore()
const emit = defineEmits(['updated'])
let querySuggestedAPIs = NewSuggestedAPIsQuery(props.store!, props.suite!)
const testResultActiveTab = ref('output')
let querySuggestedAPIs = NewSuggestedAPIsQuery(store.name!, props.suite!)
const testResultActiveTab = ref(Cache.GetPreference().responseActiveTab)
watch(testResultActiveTab, Cache.WatchResponseActiveTab)
const requestLoading = ref(false)
const testResult = ref({ header: [] as Pair[] } as TestResult)
const sendRequest = async () => {
@ -34,7 +37,7 @@ const sendRequest = async () => {
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
'X-Store-Name': store.name
},
body: JSON.stringify({
suite: suite,
@ -61,9 +64,10 @@ const sendRequest = async () => {
}
if (e.body !== '') {
testResult.value.bodyObject = JSON.parse(e.body)
testResult.value.originBodyObject = JSON.parse(e.body)
}
SetTestCaseResponseCache(suite + '-' + name, {
Cache.SetTestCaseResponseCache(suite + '-' + name, {
body: testResult.value.bodyObject,
output: e.output,
statusCode: testResult.value.statusCode
@ -77,16 +81,31 @@ const sendRequest = async () => {
requestLoading.value = false
ElMessage.error('Oops, ' + e)
testResult.value.bodyObject = JSON.parse(e.body)
testResult.value.originBodyObject = JSON.parse(e.body)
})
}
const responseBodyFilterText = ref('')
function responseBodyFilter() {
if (responseBodyFilterText.value === '') {
testResult.value.bodyObject = testResult.value.originBodyObject
} else {
const query = JSONPath({
path: responseBodyFilterText.value,
json: testResult.value.originBodyObject,
resultType: 'value'
})
testResult.value.bodyObject = query[0]
}
}
const parameterDialogOpened = ref(false)
const parameters = ref([] as Pair[])
function openParameterDialog() {
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
'X-Store-Name': store.name
},
body: JSON.stringify({
name: props.suite
@ -113,7 +132,7 @@ function generateCode() {
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
'X-Store-Name': store.name
},
body: JSON.stringify({
TestSuite: suite,
@ -200,7 +219,7 @@ function load() {
}
// load cache
const cache = GetTestCaseResponseCache(suite + '-' + name)
const cache = Cache.GetTestCaseResponseCache(suite + '-' + name)
if (cache.body) {
testResult.value.bodyObject = cache.body
testResult.value.output = cache.output
@ -210,11 +229,12 @@ function load() {
testResult.value.output = ''
testResult.value.statusCode = 0
}
testResult.value.originBodyObject = testResult.value.bodyObject
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
'X-Store-Name': store.name
},
body: JSON.stringify({
suite: suite,
@ -291,7 +311,7 @@ function saveTestCase(tip: boolean = true) {
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
'X-Store-Name': store.name
},
body: JSON.stringify(testCaseWithSuite.value)
}
@ -318,7 +338,7 @@ function deleteTestCase() {
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
'X-Store-Name': store.name
},
body: JSON.stringify({
suite: suite,
@ -366,7 +386,8 @@ watch(currentCodeGenerator, () => {
})
const options = GetHTTPMethods()
const activeName = ref('body')
const requestActiveTab = ref(Cache.GetPreference().requestActiveTab)
watch(requestActiveTab, Cache.WatchRequestActiveTab)
function bodyFiledExpectChange() {
const data = testCaseWithSuite.value.data.response.bodyFieldsExpect
@ -468,7 +489,7 @@ const pupularHeaders = ref([] as Pair[])
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
'X-Store-Name': store.name
},
}
fetch('/server.Runner/PopularHeaders', requestOptions)
@ -494,8 +515,11 @@ const queryPupularHeaders = (queryString: string, cb: (arg: any) => void) => {
<el-header style="padding-left: 5px">
<div style="margin-bottom: 5px">
<el-button type="primary" @click="saveTestCase" :icon="Edit" :loading="saveLoading"
>{{ t('button.save') }}</el-button
>
disabled v-if="store.readOnly"
>{{ t('button.save') }}</el-button>
<el-button type="primary" @click="saveTestCase" :icon="Edit" :loading="saveLoading"
v-if="!store.readOnly"
>{{ t('button.save') }}</el-button>
<el-button type="primary" @click="deleteTestCase" :icon="Delete">{{ t('button.delete') }}</el-button>
<el-button type="primary" @click="openCodeDialog">{{ t('button.generateCode') }}</el-button>
</div>
@ -537,7 +561,7 @@ const queryPupularHeaders = (queryString: string, cb: (arg: any) => void) => {
</el-header>
<el-main>
<el-tabs v-model="activeName" class="demo-tabs">
<el-tabs v-model="requestActiveTab" class="demo-tabs">
<el-tab-pane label="Query" name="query" v-if="props.kindName !== 'tRPC' && props.kindName !== 'gRPC'">
<el-table :data="testCaseWithSuite.data.request.query" style="width: 100%">
<el-table-column label="Key" width="180">
@ -767,6 +791,8 @@ const queryPupularHeaders = (queryString: string, cb: (arg: any) => void) => {
/>
</el-tab-pane>
<el-tab-pane label="Body" name="body">
<el-input :prefix-icon="Search" @change="responseBodyFilter" v-model="responseBodyFilterText"
clearable label="dddd" placeholder="$.key" />
<JsonViewer :value="testResult.bodyObject" :expand-depth="5" copyable boxed sort />
</el-tab-pane>
<el-tab-pane name="response-header">

View File

@ -5,16 +5,17 @@ import { Edit } from '@element-plus/icons-vue'
import type { FormInstance, FormRules } from 'element-plus'
import type { Suite, TestCase, Pair } from './types'
import { NewSuggestedAPIsQuery } from './types'
import { Cache } from './cache'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const props = defineProps({
name: String,
store: String,
})
const store = Cache.GetCurrentStore()
const emit = defineEmits(['updated'])
let querySuggestedAPIs = NewSuggestedAPIsQuery(props.store!, props.name!)
let querySuggestedAPIs = NewSuggestedAPIsQuery(store.name, props.name!)
const suite = ref({
name: '',
@ -30,12 +31,12 @@ const suite = ref({
}
} as Suite)
function load() {
if (!props.name || props.store === "") return
if (!props.name || store.name === "") return
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
'X-Store-Name': store.name
},
body: JSON.stringify({
name: props.name
@ -70,7 +71,7 @@ function save() {
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
'X-Store-Name': store.name
},
body: JSON.stringify(suite.value)
}
@ -108,7 +109,7 @@ const rules = reactive<FormRules<Suite>>({
function openNewTestCaseDialog() {
dialogVisible.value = true
querySuggestedAPIs = NewSuggestedAPIsQuery(props.store!, props.name!)
querySuggestedAPIs = NewSuggestedAPIsQuery(store.name!, props.name!)
}
const submitForm = async (formEl: FormInstance | undefined) => {
@ -120,7 +121,7 @@ const submitForm = async (formEl: FormInstance | undefined) => {
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
'X-Store-Name': store.name
},
body: JSON.stringify({
suiteName: props.name,
@ -150,7 +151,7 @@ function del() {
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
'X-Store-Name': store.name
},
body: JSON.stringify({
name: props.name
@ -174,7 +175,7 @@ function convert() {
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
'X-Store-Name': store.name
},
body: JSON.stringify({
Generator: 'jmeter',
@ -294,7 +295,8 @@ function paramChange() {
<el-divider />
</div>
<el-button type="primary" @click="save">{{ t('button.save') }}</el-button>
<el-button type="primary" @click="save" v-if="!store.readOnly">{{ t('button.save') }}</el-button>
<el-button type="primary" @click="save" disabled v-if="store.readOnly">{{ t('button.save') }}</el-button>
<el-button type="primary" @click="del" test-id="suite-del-but">{{ t('button.delete') }}</el-button>
<el-button type="primary" @click="openNewTestCaseDialog" :icon="Edit" test-id="open-new-case-dialog">{{ t('button.newtestcase') }}</el-button>
<el-button type="primary" @click="convert" test-id="convert">{{ t('button.export') }}</el-button>

View File

@ -4,6 +4,22 @@ export interface TestCaseResponse {
statusCode: number
}
interface Store {
name: string
readOnly: boolean
}
interface Stores {
current: string
items: Store[]
}
export interface Preference {
darkTheme: boolean
requestActiveTab: string,
responseActiveTab: string
}
export function GetTestCaseResponseCache(id: string) {
const val = sessionStorage.getItem(id)
if (val && val !== '') {
@ -34,3 +50,84 @@ export function SetLastTestCaseLocation(suite: string, testcase: string) {
}))
return
}
const preferenceKey = "api-testing-preference"
export function GetPreference() {
const val = localStorage.getItem(preferenceKey)
if (val && val !== '') {
return JSON.parse(val)
} else {
return {
darkTheme: false,
requestActiveTab: "body",
responseActiveTab: "body"
} as Preference
}
}
export function SetPreference(preference: Preference) {
localStorage.setItem(preferenceKey, JSON.stringify(preference))
return
}
export function WatchRequestActiveTab(tab: string) {
const preference = GetPreference()
preference.requestActiveTab = tab
SetPreference(preference)
}
function WatchResponseActiveTab(tab: string) {
const preference = GetPreference()
preference.responseActiveTab = tab
SetPreference(preference)
}
function WatchDarkTheme(darkTheme: boolean) {
const preference = GetPreference()
preference.darkTheme = darkTheme
SetPreference(preference)
}
const storeKey = "stores"
function GetCurrentStore() {
const val = sessionStorage.getItem(storeKey)
if (val && val !== '') {
const stores = JSON.parse(val)
for (var i = 0; i < stores.items.length; i++) {
if (stores.items[i].name === stores.current) {
return stores.items[i]
}
}
}
return {}
}
function SetCurrentStore(name: string) {
const val = sessionStorage.getItem(storeKey)
if (val && val !== '') {
const stores = JSON.parse(val)
stores.current = name
SetStores(stores)
}
}
function SetStores(stores: Stores | Store[]) {
if ('current' in stores) {
sessionStorage.setItem(storeKey, JSON.stringify(stores))
} else {
sessionStorage.setItem(storeKey, JSON.stringify({
items: stores
}))
}
return
}
export const Cache = {
GetTestCaseResponseCache,
SetTestCaseResponseCache,
GetLastTestCaseLocation,
SetLastTestCaseLocation,
GetPreference,
WatchRequestActiveTab,
WatchResponseActiveTab,
WatchDarkTheme,
GetCurrentStore, SetStores, SetCurrentStore
}

View File

@ -18,6 +18,9 @@ export interface TestResult {
error: string
statusCode: number
header: Pair[]
// inner fileds
originBodyObject:{}
}
export interface Pair {

View File

@ -198,7 +198,7 @@ tiup playground --db.host 0.0.0.0
mkdir bin
echo "- name: db
kind:
name: database
name: atest-store-orm
url: localhost:7071
url: localhost:4000
username: root
@ -233,15 +233,16 @@ Have a look at the expected configuration below:
- name: s3
url: http://172.11.0.13:30999 # address of the s3 server
kind:
name: s3
name: atest-store-s3
url: localhost:7072 # address of the s3 storage extension
properties:
accessKeyID: 6e03rIMChrsZ6YZl
secretAccessKey: F0xH6o2qRYTyAUyRuXO81B4gj7zUrSaj
sessiontoken: ""
region: cn
disableSSL: true
forcepathstyle: true
bucket: vm1
region: cn
```
### Git Storage
@ -261,10 +262,13 @@ Have a look at the expected configuration below:
username: linuxsuren
password: linuxsuren
kind:
name: git
name: atest-store-git # the extension binary file name
url: localhost:7074 # address of the git storage extension
properties:
targetPath: .
properties: # optional properties for specific features
targetPath: . # target path to find YAML files
name: linuxsuren # the name for git commit
email: linuxsuren@github.com # the email address for git commit
insecure: false # whether to use insecure
```
## Secret Server

View File

@ -0,0 +1,12 @@
version: '3.1'
services:
testing:
image: "linuxsuren.docker.scarf.sh/linuxsuren/api-testing:master"
ports:
- 8080:8080
etcd:
image: "bitnami/etcd:3.5.10"
expose:
- "2379"
environment:
ALLOW_NONE_AUTHENTICATION: "yes"

View File

@ -1,9 +1,6 @@
FROM bitnami/etcd:3.5.10 AS etcd
FROM ghcr.io/linuxsuren/api-testing:master
WORKDIR /workspace
COPY --from=etcd /opt/bitnami/etcd/bin/etcd /usr/local/bin/etcd
COPY . .
RUN chmod +x entrypoint.sh

View File

@ -1,15 +1,92 @@
version: '3.1'
services:
testing:
build: .
environment:
GITEE_TOKEN: "$GITEE_TOKEN"
depends_on:
etcd:
condition: service_healthy
mysql:
condition: service_healthy
mariadb:
condition: service_healthy
# postgres:
# condition: service_healthy
# clickhouse:
# condition: service_healthy
# minio:
# condition: service_started
links:
- etcd
- mysql
- mariadb
# - minio
# - postgres
# - clickhouse
etcd:
image: "bitnami/etcd:3.5.10"
expose:
- "2379"
environment:
ALLOW_NONE_AUTHENTICATION: "yes"
# depends_on:
# - etcd
# links:
# - etcd
# etcd:
# image: "bitnami/etcd:3.5.10"
# expose:
# - "2379"
healthcheck:
test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/2379"]
interval: 3s
timeout: 30s
retries: 10
start_period: 3s
mysql:
image: mysql:8.2.0
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: atest
healthcheck:
test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/3306"]
interval: 3s
timeout: 30s
retries: 10
start_period: 3s
mariadb:
image: mariadb:11.0
environment:
MARIADB_ROOT_PASSWORD: root
MARIADB_DATABASE: atest
healthcheck:
test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/3306"]
interval: 3s
timeout: 30s
retries: 10
start_period: 3s
minio:
image: bitnami/minio:2023.11.6
environment:
MINIO_ROOT_USER: root
MINIO_ROOT_PASSWORD: root
MINIO_SERVER_HOST: minio
MINIO_DEFAULT_BUCKETS: bucket
# postgres:
# image: postgres:16.0
# environment:
# ALLOW_NONE_AUTHENTICATION: "yes"
# POSTGRES_USER: root
# POSTGRES_PASSWORD: root
# POSTGRES_DB: atest
# healthcheck:
# test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5432"]
# interval: 3s
# timeout: 30s
# retries: 10
# start_period: 3s
# clickhouse:
# image: bitnami/clickhouse:23.10.2
# environment:
# CLICKHOUSE_ADMIN_USER: root
# CLICKHOUSE_ADMIN_PASSWORD: root
# CLICKHOUSE_MYSQL_PORT: 9004
# healthcheck:
# test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/9004"]
# interval: 3s
# timeout: 30s
# retries: 10
# start_period: 3s

View File

@ -3,10 +3,23 @@ set -e
mkdir -p /root/.config/atest
nohup etcd&
nohup atest server&
atest run -p etcd.yaml
atest run -p git.yaml
kind=orm target=mysql:3306 driver=mysql atest run -p test-suite-common.yaml
kind=orm target=mariadb:3306 driver=mysql atest run -p test-suite-common.yaml
kind=etcd target=etcd:2379 atest run -p test-suite-common.yaml
if [ -z "$GITEE_TOKEN" ]
then
atest run -p git.yaml
else
echo "found gitee token"
kind=git target=https://gitee.com/linuxsuren/test username=linuxsuren password=$GITEE_TOKEN atest run -p test-suite-common.yaml
fi
# TODO need to fix below cases
# kind=orm target=postgres:5432 driver=postgres atest run -p test-suite-common.yaml
# kind=orm target=clickhouse:9004 driver=mysql dbname=default atest run -p test-suite-common.yaml
# kind=s3 target=minio:9000 atest run -p test-suite-common.yaml
cat /root/.config/atest/stores.yaml
exit 0

25
extensions/e2e/start.sh Executable file
View File

@ -0,0 +1,25 @@
#!/bin/bash
docker-compose version
docker-compose up --build -d
while true
do
docker-compose ps | grep testing
if [ $? -eq 1 ]
then
code=-1
docker-compose logs | grep e2e-testing
docker-compose logs | grep e2e-testing | grep Usage
if [ $? -eq 1 ]
then
code=0
echo "successed"
fi
docker-compose down
set -e
exit $code
fi
sleep 1
done

View File

@ -7,22 +7,43 @@ api: |
param:
suiteName: "{{randAlpha 6}}"
caseName: "{{randAlpha 6}}"
store: "{{randAlpha 3}}"
items:
- name: CreateStore
before:
items:
- httpReady("http://localhost:8080/healthz", 2400)
- httpReady("http://localhost:2379/health", 2400)
request:
api: /CreateStore
method: POST
body: |
{
"name": "etcd",
"url": "localhost:2379",
"name": "{{.param.store}}",
"url": "{{env "target"}}",
"username": "{{default "root" (env "username")}}",
"password": "{{default "root" (env "password")}}",
"kind": {
"name": "atest-store-etcd"
}
"name": "atest-store-{{env "kind"}}"
},
"properties": [{
"key": "driver",
"value": "{{default "mysql" (env "driver")}}"
}, {
"key": "database",
"value": "{{default "atest" (env "dbname")}}"
}, {
"key": "bucket",
"value": "bucket"
}, {
"key": "region",
"value": "cn"
}, {
"key": "disablessl",
"value": "true"
}, {
"key": "targetPath",
"value": "api-testing"
}]
}
- name: createSuite
before:
@ -32,7 +53,7 @@ items:
api: /CreateTestSuite
method: POST
header:
X-Store-Name: etcd
X-Store-Name: "{{.param.store}}"
body: |
{
"name": "{{.param.suiteName}}",
@ -43,7 +64,7 @@ items:
api: /UpdateTestSuite
method: POST
header:
X-Store-Name: etcd
X-Store-Name: "{{.param.store}}"
body: |
{
"name": "{{.param.suiteName}}",
@ -58,7 +79,7 @@ items:
api: /GetTestSuite
method: POST
header:
X-Store-Name: etcd
X-Store-Name: "{{.param.store}}"
body: |
{
"name": "{{.param.suiteName}}"
@ -71,7 +92,7 @@ items:
api: /CreateTestCase
method: POST
header:
X-Store-Name: etcd
X-Store-Name: "{{.param.store}}"
body: |
{
"suiteName": "{{.param.suiteName}}",
@ -82,7 +103,7 @@ items:
"method": "POST",
"header": [{
"name": "X-Store-Name",
"value": "etcd"
"value": "{{.param.store}}"
}]
}
}
@ -92,7 +113,7 @@ items:
api: /UpdateTestCase
method: POST
header:
X-Store-Name: etcd
X-Store-Name: "{{.param.store}}"
body: |
{
"suiteName": "{{.param.suiteName}}",
@ -103,7 +124,7 @@ items:
"method": "POST",
"header": [{
"name": "X-Store-Name",
"value": "etcd"
"value": "{{.param.store}}"
}],
"body": "good"
}
@ -114,7 +135,7 @@ items:
api: /GetTestCase
method: POST
header:
X-Store-Name: etcd
X-Store-Name: "{{.param.store}}"
body: |
{
"suite": "{{.param.suiteName}}",
@ -128,7 +149,7 @@ items:
api: /DeleteTestCase
method: POST
header:
X-Store-Name: etcd
X-Store-Name: "{{.param.store}}"
body: |
{
"suite": "{{.param.suiteName}}",

View File

@ -41,17 +41,15 @@ func NewRootCommand() (c *cobra.Command) {
RunE: opt.runE,
}
opt.AddFlags(c.Flags())
c.Flags().StringVarP(&opt.endpoint, "endpoint", "", "", "The etcd server endpoint")
return
}
type options struct {
*ext.Extension
endpoint string
}
func (o *options) runE(c *cobra.Command, _ []string) (err error) {
remoteServer := pkg.NewRemoteServer(o.endpoint, pkg.NewRealEtcd())
remoteServer := pkg.NewRemoteServer(pkg.NewRealEtcd())
err = ext.CreateRunner(o.Extension, c, remoteServer)
return
}

View File

@ -33,19 +33,18 @@ import (
"github.com/linuxsuren/api-testing/pkg/server"
"github.com/linuxsuren/api-testing/pkg/testing"
"github.com/linuxsuren/api-testing/pkg/testing/remote"
"github.com/linuxsuren/api-testing/pkg/version"
clientv3 "go.etcd.io/etcd/client/v3"
)
type remoteserver struct {
endpoint string
kvFactory KVFactory
remote.UnimplementedLoaderServer
}
// NewRemoteServer creates a remote server instance
func NewRemoteServer(endpoint string, kvFactory KVFactory) remote.LoaderServer {
func NewRemoteServer(kvFactory KVFactory) remote.LoaderServer {
return &remoteserver{
endpoint: endpoint,
kvFactory: kvFactory,
}
}
@ -245,8 +244,10 @@ func (s *remoteserver) DeleteTestCase(ctx context.Context, testcase *server.Test
}
return
}
func (s *remoteserver) Verify(ctx context.Context, in *server.Empty) (reply *server.CommonResult, err error) {
reply = &server.CommonResult{}
func (s *remoteserver) Verify(ctx context.Context, in *server.Empty) (reply *server.ExtensionStatus, err error) {
reply = &server.ExtensionStatus{
Version: version.GetVersion(),
}
var cli SimpleKV
cli, err = s.getClient(ctx)
@ -256,9 +257,18 @@ func (s *remoteserver) Verify(ctx context.Context, in *server.Empty) (reply *ser
}
defer cli.Close()
reply.Success = true
// try to connect
if _, err = cli.Get(ctx, keyPrefix, connectTestOption()...); err == nil {
reply.Ready = true
}
return
}
func connectTestOption() []clientv3.OpOption {
return []clientv3.OpOption{clientv3.WithLimit(1), clientv3.WithPrefix(),
clientv3.WithCountOnly(), clientv3.WithKeysOnly()}
}
func getTestCase(ctx context.Context, cli SimpleKV, suiteName, caseName string) (testcase *testing.TestCase, index int, testsuite *testing.TestSuite, err error) {
index = NotFound
var resp *clientv3.GetResponse

View File

@ -39,7 +39,7 @@ func TestRemoteServer(t *testing.T) {
ctx := remote.WithIncomingStoreContext(context.Background(), &atest.Store{
Name: "test",
})
remoteServer := NewRemoteServer("endpoint", &fakeKV{
remoteServer := NewRemoteServer(&fakeKV{
data: map[string]string{},
})
@ -128,7 +128,7 @@ func TestRemoteServer(t *testing.T) {
verifyResult, err := remoteServer.Verify(ctx, &server.Empty{})
if assert.NoError(t, err) {
assert.True(t, verifyResult.Success)
assert.True(t, verifyResult.Ready)
}
}

View File

@ -41,6 +41,7 @@ import (
"github.com/linuxsuren/api-testing/pkg/testing"
"github.com/linuxsuren/api-testing/pkg/testing/remote"
"github.com/linuxsuren/api-testing/pkg/util"
"github.com/linuxsuren/api-testing/pkg/version"
)
type gitClient struct {
@ -250,11 +251,18 @@ func (s *gitClient) DeleteTestCase(ctx context.Context, testcase *server.TestCas
}
return
}
func (s *gitClient) Verify(ctx context.Context, in *server.Empty) (reply *server.CommonResult, err error) {
func (s *gitClient) Verify(ctx context.Context, in *server.Empty) (reply *server.ExtensionStatus, err error) {
_, clientErr := s.ListTestSuite(ctx, in)
reply = &server.CommonResult{
Success: clientErr == nil,
reply = &server.ExtensionStatus{
Ready: clientErr == nil,
Message: util.OKOrErrorMessage(clientErr),
Version: version.GetVersion(),
}
// no git repository allows to write files without authentication
store := remote.GetStoreFromContext(ctx)
if store != nil {
reply.ReadOnly = store.Username == "" || store.Password == ""
}
return
}

View File

@ -61,7 +61,8 @@ func TestGetClient(t *testing.T) {
result, err := gitClient.Verify(ctx, &server.Empty{})
assert.NoError(t, err)
assert.False(t, result.Success)
assert.False(t, result.Ready)
assert.True(t, result.ReadOnly)
})
t.Run("ListTestSuite", func(t *testing.T) {

View File

@ -1,4 +1,5 @@
/**
/*
*
MIT License
Copyright (c) 2023 API Testing Authors.

View File

@ -1,3 +1,27 @@
/**
MIT License
Copyright (c) 2023 API Testing Authors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package pkg
import (

View File

@ -1,3 +1,27 @@
/**
MIT License
Copyright (c) 2023 API Testing Authors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package pkg_test
import (

View File

@ -21,16 +21,20 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package pkg
import (
"context"
"errors"
"fmt"
"log"
"strings"
"github.com/linuxsuren/api-testing/pkg/server"
"github.com/linuxsuren/api-testing/pkg/testing/remote"
"github.com/linuxsuren/api-testing/pkg/util"
"github.com/linuxsuren/api-testing/pkg/version"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
@ -46,9 +50,23 @@ func NewRemoteServer() (s remote.LoaderServer) {
return
}
func createDB(user, address, database string) (db *gorm.DB, err error) {
dsn := fmt.Sprintf("%s:@tcp(%s)/%s?charset=utf8mb4", user, address, database)
fmt.Println("try to connect to", dsn)
func createDB(user, password, address, database, driver string) (db *gorm.DB, err error) {
var dsn string
switch driver {
case "mysql", "":
dsn = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4", user, password, address, database)
case "postgres":
obj := strings.Split(address, ":")
host, port := obj[0], obj[1]
dsn = fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Shanghai", host, user, password, database, port)
case "clickhouse":
dsn = fmt.Sprintf("tcp://%s?database=%s&username=%s&password=%s&read_timeout=10&write_timeout=20", address, database, user, password)
default:
err = fmt.Errorf("invalid database driver %q", driver)
return
}
log.Printf("try to connect to %q", dsn)
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
@ -75,13 +93,15 @@ func (s *dbserver) getClient(ctx context.Context) (db *gorm.DB, err error) {
}
database := "atest"
for key, val := range store.Properties {
if key == "database" {
database = val
driver := "mysql"
if v, ok := store.Properties["database"]; ok && v != "" {
database = v
}
if v, ok := store.Properties["driver"]; ok && v != "" {
driver = v
}
if db, err = createDB(store.Username, store.URL, database); err == nil {
if db, err = createDB(store.Username, store.Password, store.URL, database, driver); err == nil {
dbCache[store.Name] = db
}
}
@ -237,11 +257,12 @@ func (s *dbserver) DeleteTestCase(ctx context.Context, testcase *server.TestCase
return
}
func (s *dbserver) Verify(ctx context.Context, in *server.Empty) (reply *server.CommonResult, err error) {
func (s *dbserver) Verify(ctx context.Context, in *server.Empty) (reply *server.ExtensionStatus, err error) {
db, clientErr := s.getClient(ctx)
reply = &server.CommonResult{
Success: err == nil && db != nil,
reply = &server.ExtensionStatus{
Ready: err == nil && db != nil,
Message: util.OKOrErrorMessage(clientErr),
Version: version.GetVersion(),
}
return
}

View File

@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package pkg
import (
@ -90,6 +91,6 @@ func TestNewRemoteServer(t *testing.T) {
t.Run("Verify", func(t *testing.T) {
reply, err := remoteServer.Verify(defaultCtx, nil)
assert.NoError(t, err)
assert.False(t, reply.Success)
assert.False(t, reply.Ready)
})
}

View File

@ -1,3 +1,27 @@
/**
MIT License
Copyright (c) 2023 API Testing Authors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package cmd
import (

View File

@ -1,3 +1,27 @@
/**
MIT License
Copyright (c) 2023 API Testing Authors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package cmd
import (

View File

@ -100,3 +100,5 @@ require (
trpc.group/trpc-go/trpc-go v1.0.1 // indirect
trpc.group/trpc/trpc-protocol/pb/go/trpc v1.0.0 // indirect
)
replace github.com/linuxsuren/api-testing => ../../.

View File

@ -1,3 +1,27 @@
/**
MIT License
Copyright (c) 2023 API Testing Authors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package pkg
import (

View File

@ -1,9 +1,34 @@
/**
MIT License
Copyright (c) 2023 API Testing Authors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package pkg
import (
"bytes"
"context"
"errors"
"log"
"strings"
"github.com/aws/aws-sdk-go/aws"
@ -14,6 +39,7 @@ import (
"github.com/linuxsuren/api-testing/pkg/testing"
"github.com/linuxsuren/api-testing/pkg/testing/remote"
"github.com/linuxsuren/api-testing/pkg/util"
"github.com/linuxsuren/api-testing/pkg/version"
"gopkg.in/yaml.v3"
)
@ -170,11 +196,12 @@ func (s *s3Client) DeleteTestCase(ctx context.Context, testcase *server.TestCase
}
return
}
func (s *s3Client) Verify(ctx context.Context, in *server.Empty) (reply *server.CommonResult, err error) {
func (s *s3Client) Verify(ctx context.Context, in *server.Empty) (reply *server.ExtensionStatus, err error) {
_, clientErr := s.ListTestSuite(ctx, in)
reply = &server.CommonResult{
Success: err == nil,
reply = &server.ExtensionStatus{
Ready: err == nil,
Message: util.OKOrErrorMessage(clientErr),
Version: version.GetVersion(),
}
return
}
@ -189,11 +216,19 @@ func (s *s3Client) getClient(ctx context.Context) (db *s3WithBucket, err error)
}
options := mapToS3Options(store.Properties)
if options.AccessKeyID == "" {
options.AccessKeyID = store.Username
}
if options.SecretAccessKey == "" {
options.SecretAccessKey = store.Password
}
log.Println("s3 server", store.URL)
var sess *session.Session
sess, err = createClientFromSs3Options(options, store.URL)
if err == nil {
svc := s.S3Creator.New(sess) // s3.New(sess)
svc := s.S3Creator.New(sess)
db = &s3WithBucket{S3API: svc, bucket: options.Bucket}
clientCache[store.Name] = db
}

View File

@ -1,3 +1,27 @@
/**
MIT License
Copyright (c) 2023 API Testing Authors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package pkg
import (
@ -32,10 +56,10 @@ func TestNewRemoteServer(t *testing.T) {
_, err := newRemoteServer(t).ListTestSuite(defaultCtx, nil)
assert.NoError(t, err)
var result *server.CommonResult
var result *server.ExtensionStatus
result, err = newRemoteServer(t).Verify(defaultCtx, &server.Empty{})
assert.NoError(t, err)
assert.True(t, result.Success)
assert.True(t, result.Ready)
})
t.Run("CreateTestSuite", func(t *testing.T) {

View File

@ -26,12 +26,14 @@ package extension
import (
"fmt"
"net"
"os"
"github.com/linuxsuren/api-testing/pkg/testing/remote"
"github.com/linuxsuren/api-testing/pkg/version"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"google.golang.org/grpc"
"net"
"os"
)
// Extension is the default command option of the extension
@ -82,7 +84,7 @@ func CreateRunner(ext *Extension, c *cobra.Command, removeServer remote.LoaderSe
gRPCServer := grpc.NewServer()
remote.RegisterLoaderServer(gRPCServer, removeServer)
c.Printf("%s is running at %s\n", ext.GetFullName(), address)
c.Printf("%s@%s is running at %s\n", ext.GetFullName(), version.GetVersion(), address)
RegisterStopSignal(c.Context(), func() {
_ = os.Remove(ext.Socket)

View File

@ -2,7 +2,7 @@ package extension
import (
"context"
"fmt"
"log"
"os"
"os/signal"
)
@ -19,11 +19,11 @@ func RegisterStopSignal(ctx context.Context, callback func(), servers ...StopAbl
case <-endChan:
case <-ctx.Done():
}
fmt.Println("Stopping the server...")
if callback != nil {
callback()
}
for _, server := range servers {
log.Println("Stopping the server...")
server.Stop()
}
}(ctx)

View File

@ -29,7 +29,7 @@ type fakeClient struct {
err error
}
func (f*fakeClient)Invoke(ctx context.Context, reqBody interface{}, rspBody interface{}, opt ...client.Option) (err error) {
func (f *fakeClient) Invoke(ctx context.Context, reqBody interface{}, rspBody interface{}, opt ...client.Option) (err error) {
err = f.err
return
}

View File

@ -18,7 +18,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package grpc_test
import (

View File

@ -4,10 +4,10 @@ import (
_ "embed"
"fmt"
"github.com/linuxsuren/api-testing/pkg/apispec"
"io"
"github.com/signintech/gopdf"
"github.com/flopp/go-findfont"
"github.com/linuxsuren/api-testing/pkg/apispec"
"github.com/signintech/gopdf"
"io"
"log"
"strconv"
)
@ -24,7 +24,6 @@ func NewPDFResultWriter(writer io.Writer) ReportResultWriter {
// Output writes the PDF base report to target writer
func (w *pdfResultWriter) Output(result []ReportResult) (err error) {
pdf := gopdf.GoPdf{}
pdf.Start(gopdf.Config{PageSize: *gopdf.PageSizeA4})
fmt.Println(findfont.List()[len(findfont.List())-1])

View File

@ -44,6 +44,7 @@ func ToGRPCStore(store testing.Store) (result *Store) {
Url: store.URL,
Username: store.Username,
Password: store.Password,
Disabled: store.Disabled,
Properties: mapToPair(store.Properties),
}
return
@ -57,6 +58,7 @@ func ToNormalStore(store *Store) (result testing.Store) {
URL: store.Url,
Username: store.Username,
Password: store.Password,
Disabled: store.Disabled,
Properties: pairToMap(store.Properties),
}
if store.Kind != nil {

View File

@ -42,6 +42,7 @@ func TestToGRPCStore(t *testing.T) {
Url: urlFoo,
Username: "user",
Password: "pass",
Disabled: true,
Properties: []*Pair{{
Key: "foo", Value: "bar",
}},
@ -55,6 +56,7 @@ func TestToGRPCStore(t *testing.T) {
URL: urlFoo,
Username: "user",
Password: "pass",
Disabled: true,
Properties: map[string]string{
"foo": "bar",
},

View File

@ -735,7 +735,8 @@ func (s *server) GetStores(ctx context.Context, in *Empty) (reply *Stores, err e
grpcStore := ToGRPCStore(item)
storeStatus, sErr := s.VerifyStore(ctx, &SimpleQuery{Name: item.Name})
grpcStore.Ready = sErr == nil && storeStatus.Success
grpcStore.Ready = sErr == nil && storeStatus.Ready
grpcStore.ReadOnly = storeStatus.ReadOnly
grpcStore.Password = "******" // return a placeholder instead of the actual value for the security reason
reply.Data = append(reply.Data, grpcStore)
@ -767,6 +768,7 @@ func (s *server) UpdateStore(ctx context.Context, in *Store) (reply *Store, err
storeFactory := testing.NewStoreFactory(s.configDir)
store := ToNormalStore(in)
if err = storeFactory.UpdateStore(store); err == nil && s.storeExtMgr != nil {
// TODO need to restart extension if config was changed
err = s.storeExtMgr.Start(store.Kind.Name, store.Kind.URL)
}
return
@ -777,13 +779,13 @@ func (s *server) DeleteStore(ctx context.Context, in *Store) (reply *Store, err
err = storeFactory.DeleteStore(in.Name)
return
}
func (s *server) VerifyStore(ctx context.Context, in *SimpleQuery) (reply *CommonResult, err error) {
// TODO need to implement
reply = &CommonResult{}
func (s *server) VerifyStore(ctx context.Context, in *SimpleQuery) (reply *ExtensionStatus, err error) {
reply = &ExtensionStatus{}
var loader testing.Writer
if loader, err = s.getLoaderByStoreName(in.Name); err == nil && loader != nil {
verifyErr := loader.Verify()
reply.Success = verifyErr == nil
readOnly, verifyErr := loader.Verify()
reply.Ready = verifyErr == nil
reply.ReadOnly = readOnly
reply.Message = util.OKOrErrorMessage(verifyErr)
}
return

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v4.22.2
// protoc v4.25.0
// source: pkg/server/server.proto
package server
@ -1585,6 +1585,8 @@ type Store struct {
Properties []*Pair `protobuf:"bytes,6,rep,name=properties,proto3" json:"properties,omitempty"`
Kind *StoreKind `protobuf:"bytes,7,opt,name=kind,proto3" json:"kind,omitempty"`
Ready bool `protobuf:"varint,8,opt,name=ready,proto3" json:"ready,omitempty"`
ReadOnly bool `protobuf:"varint,9,opt,name=readOnly,proto3" json:"readOnly,omitempty"`
Disabled bool `protobuf:"varint,10,opt,name=disabled,proto3" json:"disabled,omitempty"`
}
func (x *Store) Reset() {
@ -1675,6 +1677,20 @@ func (x *Store) GetReady() bool {
return false
}
func (x *Store) GetReadOnly() bool {
if x != nil {
return x.ReadOnly
}
return false
}
func (x *Store) GetDisabled() bool {
if x != nil {
return x.Disabled
}
return false
}
type StoreKinds struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -2107,6 +2123,77 @@ func (x *Secret) GetDescription() string {
return ""
}
type ExtensionStatus struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Ready bool `protobuf:"varint,1,opt,name=ready,proto3" json:"ready,omitempty"`
ReadOnly bool `protobuf:"varint,2,opt,name=readOnly,proto3" json:"readOnly,omitempty"`
Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"`
Message string `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty"`
}
func (x *ExtensionStatus) Reset() {
*x = ExtensionStatus{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_server_server_proto_msgTypes[33]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ExtensionStatus) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ExtensionStatus) ProtoMessage() {}
func (x *ExtensionStatus) ProtoReflect() protoreflect.Message {
mi := &file_pkg_server_server_proto_msgTypes[33]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ExtensionStatus.ProtoReflect.Descriptor instead.
func (*ExtensionStatus) Descriptor() ([]byte, []int) {
return file_pkg_server_server_proto_rawDescGZIP(), []int{33}
}
func (x *ExtensionStatus) GetReady() bool {
if x != nil {
return x.Ready
}
return false
}
func (x *ExtensionStatus) GetReadOnly() bool {
if x != nil {
return x.ReadOnly
}
return false
}
func (x *ExtensionStatus) GetVersion() string {
if x != nil {
return x.Version
}
return ""
}
func (x *ExtensionStatus) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
type Empty struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -2116,7 +2203,7 @@ type Empty struct {
func (x *Empty) Reset() {
*x = Empty{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_server_server_proto_msgTypes[33]
mi := &file_pkg_server_server_proto_msgTypes[34]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -2129,7 +2216,7 @@ func (x *Empty) String() string {
func (*Empty) ProtoMessage() {}
func (x *Empty) ProtoReflect() protoreflect.Message {
mi := &file_pkg_server_server_proto_msgTypes[33]
mi := &file_pkg_server_server_proto_msgTypes[34]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -2142,7 +2229,7 @@ func (x *Empty) ProtoReflect() protoreflect.Message {
// Deprecated: Use Empty.ProtoReflect.Descriptor instead.
func (*Empty) Descriptor() ([]byte, []int) {
return file_pkg_server_server_proto_rawDescGZIP(), []int{33}
return file_pkg_server_server_proto_rawDescGZIP(), []int{34}
}
var File_pkg_server_server_proto protoreflect.FileDescriptor
@ -2316,7 +2403,7 @@ var file_pkg_server_server_proto_rawDesc = []byte{
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22,
0x2b, 0x0a, 0x06, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x04, 0x64, 0x61, 0x74,
0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xf2, 0x01, 0x0a,
0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xaa, 0x02, 0x0a,
0x05, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
@ -2332,164 +2419,175 @@ var file_pkg_server_server_proto_rawDesc = []byte{
0x28, 0x0b, 0x32, 0x11, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72,
0x65, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x72,
0x65, 0x61, 0x64, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x65, 0x61, 0x64,
0x79, 0x22, 0x33, 0x0a, 0x0a, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x73, 0x12,
0x25, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x69, 0x6e, 0x64,
0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x4b, 0x0a, 0x09, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4b,
0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61,
0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62,
0x6c, 0x65, 0x64, 0x22, 0x42, 0x0a, 0x0c, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73,
0x75, 0x6c, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01,
0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a,
0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x2e, 0x0a, 0x0a, 0x53, 0x69, 0x6d, 0x70, 0x6c,
0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x69,
0x72, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x20, 0x0a, 0x0a, 0x53, 0x69, 0x6d, 0x70, 0x6c,
0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x6d, 0x0a, 0x13, 0x43, 0x6f, 0x64,
0x65, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x1c, 0x0a, 0x09, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x09, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x1a,
0x0a, 0x08, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x47, 0x65,
0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x47,
0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x22, 0x2d, 0x0a, 0x07, 0x53, 0x65, 0x63, 0x72,
0x65, 0x74, 0x73, 0x12, 0x22, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65,
0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x54, 0x0a, 0x06, 0x53, 0x65, 0x63, 0x72, 0x65,
0x74, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x44,
0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x07, 0x0a,
0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x32, 0x86, 0x0f, 0x0a, 0x06, 0x52, 0x75, 0x6e, 0x6e, 0x65,
0x72, 0x12, 0x2d, 0x0a, 0x03, 0x52, 0x75, 0x6e, 0x12, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00,
0x12, 0x2c, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x12, 0x0d, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0e, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x22, 0x00, 0x12, 0x42,
0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74,
0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53,
0x75, 0x69, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79,
0x22, 0x00, 0x12, 0x42, 0x0a, 0x0f, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x65, 0x73, 0x74,
0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54,
0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0x14,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65,
0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, 0x65, 0x73,
0x79, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x18, 0x09, 0x20,
0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x1a, 0x0a,
0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52,
0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x33, 0x0a, 0x0a, 0x53, 0x74, 0x6f,
0x72, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x73, 0x12, 0x25, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18,
0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53,
0x74, 0x6f, 0x72, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x4b,
0x0a, 0x09, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72,
0x6c, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01,
0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x42, 0x0a, 0x0c, 0x43,
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73,
0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75,
0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22,
0x2e, 0x0a, 0x0a, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x20, 0x0a,
0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22,
0x20, 0x0a, 0x0a, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x22, 0x6d, 0x0a, 0x13, 0x43, 0x6f, 0x64, 0x65, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74,
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x54, 0x65, 0x73, 0x74,
0x53, 0x75, 0x69, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x54, 0x65, 0x73,
0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61,
0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61,
0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72,
0x22, 0x2d, 0x0a, 0x07, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x22, 0x0a, 0x04, 0x64,
0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22,
0x54, 0x0a, 0x06, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d,
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a,
0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x56, 0x61,
0x6c, 0x75, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69,
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x77, 0x0a, 0x0f, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x61, 0x64,
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x12, 0x1a,
0x0a, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08,
0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18,
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x07,
0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x32, 0x89, 0x0f, 0x0a, 0x06, 0x52, 0x75, 0x6e, 0x6e,
0x65, 0x72, 0x12, 0x2d, 0x0a, 0x03, 0x52, 0x75, 0x6e, 0x12, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x1a, 0x12, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22,
0x00, 0x12, 0x2c, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x12, 0x0d,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0e, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x22, 0x00, 0x12,
0x42, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69,
0x74, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74,
0x53, 0x75, 0x69, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x1a, 0x12, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c,
0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x0f, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x65, 0x73,
0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a,
0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52,
0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, 0x65,
0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x74, 0x79, 0x1a, 0x11, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74,
0x53, 0x75, 0x69, 0x74, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74,
0x65, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x11, 0x2e, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x1a, 0x12, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c,
0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x73,
0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74,
0x79, 0x1a, 0x11, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53,
0x75, 0x69, 0x74, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x11, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x1a, 0x12, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79,
0x22, 0x00, 0x12, 0x42, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74,
0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54,
0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79,
0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52,
0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65,
0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74,
0x79, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x75, 0x69, 0x74, 0x65,
0x22, 0x00, 0x12, 0x42, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74,
0x65, 0x64, 0x41, 0x50, 0x49, 0x73, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74,
0x79, 0x1a, 0x11, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43,
0x61, 0x73, 0x65, 0x73, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0b, 0x52, 0x75, 0x6e, 0x54, 0x65, 0x73,
0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54,
0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x1a,
0x16, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73,
0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x0b, 0x47, 0x65, 0x74,
0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x74, 0x79, 0x1a, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74,
0x43, 0x61, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x57, 0x69, 0x74, 0x68, 0x53, 0x75,
0x69, 0x74, 0x65, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c,
0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0e, 0x55, 0x70, 0x64,
0x61, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x57, 0x69, 0x74,
0x68, 0x53, 0x75, 0x69, 0x74, 0x65, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x0e,
0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x18,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65,
0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x38,
0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61,
0x74, 0x6f, 0x72, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70,
0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x69, 0x6d, 0x70,
0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x0c, 0x47, 0x65, 0x6e, 0x65,
0x72, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x54,
0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x74, 0x79, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x75, 0x69, 0x74,
0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73,
0x74, 0x65, 0x64, 0x41, 0x50, 0x49, 0x73, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x74, 0x79, 0x1a, 0x11, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74,
0x43, 0x61, 0x73, 0x65, 0x73, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0b, 0x52, 0x75, 0x6e, 0x54, 0x65,
0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79,
0x1a, 0x16, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61,
0x73, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x0b, 0x47, 0x65,
0x74, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x74, 0x79, 0x1a, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73,
0x74, 0x43, 0x61, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x57, 0x69, 0x74, 0x68, 0x53,
0x75, 0x69, 0x74, 0x65, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65,
0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0e, 0x55, 0x70,
0x64, 0x61, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x19, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x57, 0x69,
0x74, 0x68, 0x53, 0x75, 0x69, 0x74, 0x65, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x40, 0x0a,
0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12,
0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73,
0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12,
0x38, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x47, 0x65, 0x6e, 0x65, 0x72,
0x61, 0x74, 0x6f, 0x72, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d,
0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x69, 0x6d,
0x70, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x0c, 0x47, 0x65, 0x6e,
0x65, 0x72, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12, 0x34,
0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x72, 0x12,
0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4c, 0x69,
0x73, 0x74, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54,
0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43,
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12, 0x34, 0x0a,
0x0d, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x72, 0x12, 0x0d,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4c, 0x69, 0x73,
0x74, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x65,
0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x43, 0x6f, 0x64, 0x65, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f,
0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12, 0x30, 0x0a, 0x0e,
0x50, 0x6f, 0x70, 0x75, 0x6c, 0x61, 0x72, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x0d,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0d, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x73, 0x22, 0x00, 0x12, 0x36,
0x0a, 0x0e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x51, 0x75, 0x65, 0x72, 0x79,
0x12, 0x13, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65,
0x51, 0x75, 0x65, 0x72, 0x79, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50,
0x61, 0x69, 0x72, 0x73, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x14, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x73, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x13,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x51, 0x75,
0x65, 0x72, 0x79, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x69,
0x72, 0x73, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x31, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x56,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12, 0x30, 0x0a,
0x0e, 0x50, 0x6f, 0x70, 0x75, 0x6c, 0x61, 0x72, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12,
0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0d,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x73, 0x22, 0x00, 0x12,
0x36, 0x0a, 0x0e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x51, 0x75, 0x65, 0x72,
0x79, 0x12, 0x13, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c,
0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x50, 0x61, 0x69, 0x72, 0x73, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x14, 0x46, 0x75, 0x6e, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x73, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12,
0x13, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x51,
0x75, 0x65, 0x72, 0x79, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x61,
0x69, 0x72, 0x73, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x31, 0x0a, 0x0a, 0x47, 0x65, 0x74,
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x06,
0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48,
0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x06, 0x53,
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45,
0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65,
0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x34, 0x0a, 0x0d, 0x47, 0x65,
0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x73, 0x12, 0x0d, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x73, 0x22, 0x00,
0x12, 0x2c, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x0d, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0e, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x22, 0x00, 0x12, 0x2d,
0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x0d, 0x2e,
0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x34, 0x0a, 0x0d, 0x47,
0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x73, 0x12, 0x0d, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x73, 0x22,
0x00, 0x12, 0x2c, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x0d,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0e, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x22, 0x00, 0x12,
0x2d, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x0d,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x1a, 0x0d, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x22, 0x00, 0x12, 0x2d,
0x0a, 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x0d, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x1a, 0x0d, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a,
0x0b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x0d, 0x2e, 0x73,
0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x0d, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x1a, 0x0d, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x0b,
0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x0d, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0b, 0x56,
0x65, 0x72, 0x69, 0x66, 0x79, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x13, 0x2e, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x1a,
0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52,
0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x65,
0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45,
0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65,
0x63, 0x72, 0x65, 0x74, 0x73, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x1a, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12,
0x36, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12,
0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x1a,
0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52,
0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74,
0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x1a, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x42,
0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69,
0x6e, 0x75, 0x78, 0x73, 0x75, 0x72, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2d, 0x74, 0x65, 0x73,
0x74, 0x69, 0x6e, 0x67, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x0b,
0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x13, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79,
0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73,
0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x0a, 0x47,
0x65, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x0c, 0x43,
0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x0e, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x1a, 0x14, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c,
0x74, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63,
0x72, 0x65, 0x74, 0x12, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63,
0x72, 0x65, 0x74, 0x1a, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d,
0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x0c, 0x55,
0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x0e, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x1a, 0x14, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c,
0x74, 0x22, 0x00, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x73, 0x75, 0x72, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69,
0x2d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -2504,7 +2602,7 @@ func file_pkg_server_server_proto_rawDescGZIP() []byte {
return file_pkg_server_server_proto_rawDescData
}
var file_pkg_server_server_proto_msgTypes = make([]protoimpl.MessageInfo, 36)
var file_pkg_server_server_proto_msgTypes = make([]protoimpl.MessageInfo, 37)
var file_pkg_server_server_proto_goTypes = []interface{}{
(*Suites)(nil), // 0: server.Suites
(*Items)(nil), // 1: server.Items
@ -2539,18 +2637,19 @@ var file_pkg_server_server_proto_goTypes = []interface{}{
(*CodeGenerateRequest)(nil), // 30: server.CodeGenerateRequest
(*Secrets)(nil), // 31: server.Secrets
(*Secret)(nil), // 32: server.Secret
(*Empty)(nil), // 33: server.Empty
nil, // 34: server.Suites.DataEntry
nil, // 35: server.TestTask.EnvEntry
(*ExtensionStatus)(nil), // 33: server.ExtensionStatus
(*Empty)(nil), // 34: server.Empty
nil, // 35: server.Suites.DataEntry
nil, // 36: server.TestTask.EnvEntry
}
var file_pkg_server_server_proto_depIdxs = []int32{
34, // 0: server.Suites.data:type_name -> server.Suites.DataEntry
35, // 0: server.Suites.data:type_name -> server.Suites.DataEntry
20, // 1: server.TestCaseIdentity.parameters:type_name -> server.Pair
20, // 2: server.TestSuite.param:type_name -> server.Pair
5, // 3: server.TestSuite.spec:type_name -> server.APISpec
7, // 4: server.APISpec.rpc:type_name -> server.RPC
6, // 5: server.APISpec.secure:type_name -> server.Secure
35, // 6: server.TestTask.env:type_name -> server.TestTask.EnvEntry
36, // 6: server.TestTask.env:type_name -> server.TestTask.EnvEntry
20, // 7: server.TestTask.parameters:type_name -> server.Pair
19, // 8: server.TestResult.testCaseResult:type_name -> server.TestCaseResult
15, // 9: server.Suite.items:type_name -> server.TestCase
@ -2574,7 +2673,7 @@ var file_pkg_server_server_proto_depIdxs = []int32{
32, // 27: server.Secrets.data:type_name -> server.Secret
1, // 28: server.Suites.DataEntry.value:type_name -> server.Items
9, // 29: server.Runner.Run:input_type -> server.TestTask
33, // 30: server.Runner.GetSuites:input_type -> server.Empty
34, // 30: server.Runner.GetSuites:input_type -> server.Empty
8, // 31: server.Runner.CreateTestSuite:input_type -> server.TestSuiteIdentity
3, // 32: server.Runner.ImportTestSuite:input_type -> server.TestSuiteSource
8, // 33: server.Runner.GetTestSuite:input_type -> server.TestSuiteIdentity
@ -2587,22 +2686,22 @@ var file_pkg_server_server_proto_depIdxs = []int32{
13, // 40: server.Runner.CreateTestCase:input_type -> server.TestCaseWithSuite
13, // 41: server.Runner.UpdateTestCase:input_type -> server.TestCaseWithSuite
2, // 42: server.Runner.DeleteTestCase:input_type -> server.TestCaseIdentity
33, // 43: server.Runner.ListCodeGenerator:input_type -> server.Empty
34, // 43: server.Runner.ListCodeGenerator:input_type -> server.Empty
30, // 44: server.Runner.GenerateCode:input_type -> server.CodeGenerateRequest
33, // 45: server.Runner.ListConverter:input_type -> server.Empty
34, // 45: server.Runner.ListConverter:input_type -> server.Empty
30, // 46: server.Runner.ConvertTestSuite:input_type -> server.CodeGenerateRequest
33, // 47: server.Runner.PopularHeaders:input_type -> server.Empty
34, // 47: server.Runner.PopularHeaders:input_type -> server.Empty
22, // 48: server.Runner.FunctionsQuery:input_type -> server.SimpleQuery
22, // 49: server.Runner.FunctionsQueryStream:input_type -> server.SimpleQuery
33, // 50: server.Runner.GetVersion:input_type -> server.Empty
33, // 51: server.Runner.Sample:input_type -> server.Empty
33, // 52: server.Runner.GetStoreKinds:input_type -> server.Empty
33, // 53: server.Runner.GetStores:input_type -> server.Empty
34, // 50: server.Runner.GetVersion:input_type -> server.Empty
34, // 51: server.Runner.Sample:input_type -> server.Empty
34, // 52: server.Runner.GetStoreKinds:input_type -> server.Empty
34, // 53: server.Runner.GetStores:input_type -> server.Empty
24, // 54: server.Runner.CreateStore:input_type -> server.Store
24, // 55: server.Runner.UpdateStore:input_type -> server.Store
24, // 56: server.Runner.DeleteStore:input_type -> server.Store
22, // 57: server.Runner.VerifyStore:input_type -> server.SimpleQuery
33, // 58: server.Runner.GetSecrets:input_type -> server.Empty
34, // 58: server.Runner.GetSecrets:input_type -> server.Empty
32, // 59: server.Runner.CreateSecret:input_type -> server.Secret
32, // 60: server.Runner.DeleteSecret:input_type -> server.Secret
32, // 61: server.Runner.UpdateSecret:input_type -> server.Secret
@ -2634,7 +2733,7 @@ var file_pkg_server_server_proto_depIdxs = []int32{
24, // 87: server.Runner.CreateStore:output_type -> server.Store
24, // 88: server.Runner.UpdateStore:output_type -> server.Store
24, // 89: server.Runner.DeleteStore:output_type -> server.Store
27, // 90: server.Runner.VerifyStore:output_type -> server.CommonResult
33, // 90: server.Runner.VerifyStore:output_type -> server.ExtensionStatus
31, // 91: server.Runner.GetSecrets:output_type -> server.Secrets
27, // 92: server.Runner.CreateSecret:output_type -> server.CommonResult
27, // 93: server.Runner.DeleteSecret:output_type -> server.CommonResult
@ -3049,6 +3148,18 @@ func file_pkg_server_server_proto_init() {
}
}
file_pkg_server_server_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ExtensionStatus); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pkg_server_server_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Empty); i {
case 0:
return &v.state
@ -3067,7 +3178,7 @@ func file_pkg_server_server_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pkg_server_server_proto_rawDesc,
NumEnums: 0,
NumMessages: 36,
NumMessages: 37,
NumExtensions: 0,
NumServices: 1,
},

View File

@ -45,7 +45,7 @@ service Runner {
rpc CreateStore(Store) returns (Store) {}
rpc UpdateStore(Store) returns (Store) {}
rpc DeleteStore(Store) returns (Store) {}
rpc VerifyStore(SimpleQuery) returns (CommonResult) {}
rpc VerifyStore(SimpleQuery) returns (ExtensionStatus) {}
// secret related interfaces
rpc GetSecrets(Empty) returns (Secrets) {}
@ -212,6 +212,8 @@ message Store {
repeated Pair properties = 6;
StoreKind kind = 7;
bool ready = 8;
bool readOnly = 9;
bool disabled = 10;
}
message StoreKinds {
@ -253,5 +255,12 @@ message Secret {
string Description = 3;
}
message ExtensionStatus {
bool ready = 1;
bool readOnly = 2;
string version = 3;
string message = 4;
}
message Empty {
}

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v4.22.2
// - protoc v4.25.0
// source: pkg/server/server.proto
package server
@ -56,7 +56,7 @@ type RunnerClient interface {
CreateStore(ctx context.Context, in *Store, opts ...grpc.CallOption) (*Store, error)
UpdateStore(ctx context.Context, in *Store, opts ...grpc.CallOption) (*Store, error)
DeleteStore(ctx context.Context, in *Store, opts ...grpc.CallOption) (*Store, error)
VerifyStore(ctx context.Context, in *SimpleQuery, opts ...grpc.CallOption) (*CommonResult, error)
VerifyStore(ctx context.Context, in *SimpleQuery, opts ...grpc.CallOption) (*ExtensionStatus, error)
// secret related interfaces
GetSecrets(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Secrets, error)
CreateSecret(ctx context.Context, in *Secret, opts ...grpc.CallOption) (*CommonResult, error)
@ -346,8 +346,8 @@ func (c *runnerClient) DeleteStore(ctx context.Context, in *Store, opts ...grpc.
return out, nil
}
func (c *runnerClient) VerifyStore(ctx context.Context, in *SimpleQuery, opts ...grpc.CallOption) (*CommonResult, error) {
out := new(CommonResult)
func (c *runnerClient) VerifyStore(ctx context.Context, in *SimpleQuery, opts ...grpc.CallOption) (*ExtensionStatus, error) {
out := new(ExtensionStatus)
err := c.cc.Invoke(ctx, "/server.Runner/VerifyStore", in, out, opts...)
if err != nil {
return nil, err
@ -429,7 +429,7 @@ type RunnerServer interface {
CreateStore(context.Context, *Store) (*Store, error)
UpdateStore(context.Context, *Store) (*Store, error)
DeleteStore(context.Context, *Store) (*Store, error)
VerifyStore(context.Context, *SimpleQuery) (*CommonResult, error)
VerifyStore(context.Context, *SimpleQuery) (*ExtensionStatus, error)
// secret related interfaces
GetSecrets(context.Context, *Empty) (*Secrets, error)
CreateSecret(context.Context, *Secret) (*CommonResult, error)
@ -526,7 +526,7 @@ func (UnimplementedRunnerServer) UpdateStore(context.Context, *Store) (*Store, e
func (UnimplementedRunnerServer) DeleteStore(context.Context, *Store) (*Store, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteStore not implemented")
}
func (UnimplementedRunnerServer) VerifyStore(context.Context, *SimpleQuery) (*CommonResult, error) {
func (UnimplementedRunnerServer) VerifyStore(context.Context, *SimpleQuery) (*ExtensionStatus, error) {
return nil, status.Errorf(codes.Unimplemented, "method VerifyStore not implemented")
}
func (UnimplementedRunnerServer) GetSecrets(context.Context, *Empty) (*Secrets, error) {

View File

@ -71,7 +71,7 @@ func (s *storeExtManager) Start(name, socket string) (err error) {
socketFile := strings.TrimPrefix(socketURL, s.socketPrefix)
s.filesNeedToBeRemoved = append(s.filesNeedToBeRemoved, socketFile)
s.extStatusMap[name] = true
if err = s.execer.RunCommand(plugin, "--socket", socketFile); err != nil {
if err = s.execer.RunCommandWithIO(plugin, "", os.Stdout, os.Stderr, "--socket", socketFile); err != nil {
log.Printf("failed to start %s, error: %v", socketURL, err)
}
}(socket, binaryPath)

View File

@ -9,7 +9,7 @@ type Loader interface {
GetCount() int
Reset()
Verify() (err error)
Verify() (readOnly bool, err error)
}
type Writer interface {

View File

@ -380,7 +380,7 @@ func (l *fileLoader) DeleteTestCase(suiteName, testcase string) (err error) {
return
}
func (l *fileLoader) Verify() (err error) {
func (l *fileLoader) Verify() (readOnly bool, err error) {
// always be okay
return
}

View File

@ -7,7 +7,6 @@ import (
"github.com/h2non/gock"
atest "github.com/linuxsuren/api-testing/pkg/testing"
atesting "github.com/linuxsuren/api-testing/pkg/testing"
"github.com/stretchr/testify/assert"
)
@ -40,7 +39,9 @@ func TestFileLoader(t *testing.T) {
}
tt.verify(t, loader)
assert.NoError(t, loader.Verify())
readonly, err := loader.Verify()
assert.NoError(t, err)
assert.False(t, readonly)
})
}
}
@ -186,7 +187,7 @@ func TestSuite(t *testing.T) {
})
assert.NoError(t, err)
var testcase atesting.TestCase
var testcase atest.TestCase
testcase, err = writer.GetTestCase("test", "login")
if assert.NoError(t, err) {
assert.Equal(t, urlTestLogin, testcase.Request.API)

View File

@ -27,6 +27,7 @@ package remote
import (
context "context"
"errors"
"time"
server "github.com/linuxsuren/api-testing/pkg/server"
"github.com/linuxsuren/api-testing/pkg/testing"
@ -187,10 +188,15 @@ func (g *gRPCLoader) DeleteSuite(name string) (err error) {
return
}
func (g *gRPCLoader) Verify() (err error) {
var result *server.CommonResult
if result, err = g.client.Verify(g.ctx, &server.Empty{}); err == nil {
if !result.Success {
func (g *gRPCLoader) Verify() (readOnly bool, err error) {
// avoid to long to wait the response
ctx, cancel := context.WithTimeout(g.ctx, time.Second*3)
defer cancel()
var result *server.ExtensionStatus
if result, err = g.client.Verify(ctx, &server.Empty{}); err == nil {
readOnly = result.ReadOnly
if !result.Ready {
err = errors.New(result.Message)
}
}

View File

@ -95,8 +95,10 @@ func TestNewGRPCLoader(t *testing.T) {
err = writer.DeleteSuite("")
assert.Error(t, err)
err = writer.Verify()
var readonly bool
readonly, err = writer.Verify()
assert.Error(t, err)
assert.False(t, readonly)
})
t.Run("NewGRPCloaderFromStore", func(t *testing.T) {

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v4.22.2
// protoc v4.25.0
// source: pkg/testing/remote/loader.proto
package remote
@ -177,7 +177,7 @@ var file_pkg_testing_remote_loader_proto_rawDesc = []byte{
0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73,
0x74, 0x43, 0x61, 0x73, 0x65, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x12, 0x0a, 0x04,
0x66, 0x75, 0x6c, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x66, 0x75, 0x6c, 0x6c,
0x32, 0xe0, 0x04, 0x0a, 0x06, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x0d, 0x4c,
0x32, 0xe3, 0x04, 0x0a, 0x06, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x0d, 0x4c,
0x69, 0x73, 0x74, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x0d, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x72, 0x65,
0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x22,
@ -212,31 +212,31 @@ var file_pkg_testing_remote_loader_proto_rawDesc = []byte{
0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12,
0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73,
0x65, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
0x22, 0x00, 0x12, 0x2f, 0x0a, 0x06, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, 0x0d, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c,
0x74, 0x22, 0x00, 0x32, 0x96, 0x02, 0x0a, 0x0d, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x53, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2d, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x65, 0x63, 0x72,
0x65, 0x74, 0x12, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, 0x72,
0x65, 0x74, 0x1a, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, 0x72,
0x65, 0x74, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65,
0x74, 0x73, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74,
0x79, 0x1a, 0x0f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65,
0x74, 0x73, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65,
0x63, 0x72, 0x65, 0x74, 0x12, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65,
0x63, 0x72, 0x65, 0x74, 0x1a, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f,
0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x0c,
0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x0e, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x1a, 0x14, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75,
0x6c, 0x74, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65,
0x63, 0x72, 0x65, 0x74, 0x12, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65,
0x63, 0x72, 0x65, 0x74, 0x1a, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f,
0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x42, 0x36, 0x5a, 0x34,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x6e, 0x75, 0x78,
0x73, 0x75, 0x72, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e,
0x67, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2f, 0x72, 0x65,
0x6d, 0x6f, 0x74, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x22, 0x00, 0x12, 0x32, 0x0a, 0x06, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, 0x0d, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x17, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74,
0x61, 0x74, 0x75, 0x73, 0x22, 0x00, 0x32, 0x96, 0x02, 0x0a, 0x0d, 0x53, 0x65, 0x63, 0x72, 0x65,
0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2d, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53,
0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53,
0x65, 0x63, 0x72, 0x65, 0x74, 0x1a, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53,
0x65, 0x63, 0x72, 0x65, 0x74, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x65,
0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45,
0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65,
0x63, 0x72, 0x65, 0x74, 0x73, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x1a, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12,
0x36, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12,
0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x1a,
0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52,
0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74,
0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x1a, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x42,
0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69,
0x6e, 0x75, 0x78, 0x73, 0x75, 0x72, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2d, 0x74, 0x65, 0x73,
0x74, 0x69, 0x6e, 0x67, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67,
0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -261,8 +261,9 @@ var file_pkg_testing_remote_loader_proto_goTypes = []interface{}{
(*server.Empty)(nil), // 5: server.Empty
(*server.Secret)(nil), // 6: server.Secret
(*server.TestCases)(nil), // 7: server.TestCases
(*server.CommonResult)(nil), // 8: server.CommonResult
(*server.ExtensionStatus)(nil), // 8: server.ExtensionStatus
(*server.Secrets)(nil), // 9: server.Secrets
(*server.CommonResult)(nil), // 10: server.CommonResult
}
var file_pkg_testing_remote_loader_proto_depIdxs = []int32{
1, // 0: remote.TestSuites.data:type_name -> remote.TestSuite
@ -295,12 +296,12 @@ var file_pkg_testing_remote_loader_proto_depIdxs = []int32{
4, // 27: remote.Loader.GetTestCase:output_type -> server.TestCase
4, // 28: remote.Loader.UpdateTestCase:output_type -> server.TestCase
5, // 29: remote.Loader.DeleteTestCase:output_type -> server.Empty
8, // 30: remote.Loader.Verify:output_type -> server.CommonResult
8, // 30: remote.Loader.Verify:output_type -> server.ExtensionStatus
6, // 31: remote.SecretService.GetSecret:output_type -> server.Secret
9, // 32: remote.SecretService.GetSecrets:output_type -> server.Secrets
8, // 33: remote.SecretService.CreateSecret:output_type -> server.CommonResult
8, // 34: remote.SecretService.DeleteSecret:output_type -> server.CommonResult
8, // 35: remote.SecretService.UpdateSecret:output_type -> server.CommonResult
10, // 33: remote.SecretService.CreateSecret:output_type -> server.CommonResult
10, // 34: remote.SecretService.DeleteSecret:output_type -> server.CommonResult
10, // 35: remote.SecretService.UpdateSecret:output_type -> server.CommonResult
20, // [20:36] is the sub-list for method output_type
4, // [4:20] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name

View File

@ -19,7 +19,7 @@ service Loader {
rpc UpdateTestCase(server.TestCase) returns (server.TestCase) {}
rpc DeleteTestCase(server.TestCase) returns (server.Empty) {}
rpc Verify(server.Empty) returns (server.CommonResult) {}
rpc Verify(server.Empty) returns (server.ExtensionStatus) {}
}
message TestSuites {

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v4.22.2
// - protoc v4.25.0
// source: pkg/testing/remote/loader.proto
package remote
@ -33,7 +33,7 @@ type LoaderClient interface {
GetTestCase(ctx context.Context, in *server.TestCase, opts ...grpc.CallOption) (*server.TestCase, error)
UpdateTestCase(ctx context.Context, in *server.TestCase, opts ...grpc.CallOption) (*server.TestCase, error)
DeleteTestCase(ctx context.Context, in *server.TestCase, opts ...grpc.CallOption) (*server.Empty, error)
Verify(ctx context.Context, in *server.Empty, opts ...grpc.CallOption) (*server.CommonResult, error)
Verify(ctx context.Context, in *server.Empty, opts ...grpc.CallOption) (*server.ExtensionStatus, error)
}
type loaderClient struct {
@ -134,8 +134,8 @@ func (c *loaderClient) DeleteTestCase(ctx context.Context, in *server.TestCase,
return out, nil
}
func (c *loaderClient) Verify(ctx context.Context, in *server.Empty, opts ...grpc.CallOption) (*server.CommonResult, error) {
out := new(server.CommonResult)
func (c *loaderClient) Verify(ctx context.Context, in *server.Empty, opts ...grpc.CallOption) (*server.ExtensionStatus, error) {
out := new(server.ExtensionStatus)
err := c.cc.Invoke(ctx, "/remote.Loader/Verify", in, out, opts...)
if err != nil {
return nil, err
@ -157,7 +157,7 @@ type LoaderServer interface {
GetTestCase(context.Context, *server.TestCase) (*server.TestCase, error)
UpdateTestCase(context.Context, *server.TestCase) (*server.TestCase, error)
DeleteTestCase(context.Context, *server.TestCase) (*server.Empty, error)
Verify(context.Context, *server.Empty) (*server.CommonResult, error)
Verify(context.Context, *server.Empty) (*server.ExtensionStatus, error)
mustEmbedUnimplementedLoaderServer()
}
@ -195,7 +195,7 @@ func (UnimplementedLoaderServer) UpdateTestCase(context.Context, *server.TestCas
func (UnimplementedLoaderServer) DeleteTestCase(context.Context, *server.TestCase) (*server.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteTestCase not implemented")
}
func (UnimplementedLoaderServer) Verify(context.Context, *server.Empty) (*server.CommonResult, error) {
func (UnimplementedLoaderServer) Verify(context.Context, *server.Empty) (*server.ExtensionStatus, error) {
return nil, status.Errorf(codes.Unimplemented, "method Verify not implemented")
}
func (UnimplementedLoaderServer) mustEmbedUnimplementedLoaderServer() {}

View File

@ -45,6 +45,8 @@ type Store struct {
URL string
Username string
Password string
ReadOnly bool
Disabled bool
Properties map[string]string
}
@ -57,6 +59,8 @@ func (s *Store) ToMap() (result map[string]string) {
"url": s.URL,
"username": s.Username,
"password": s.Password,
"readonly": fmt.Sprintf("%t", s.ReadOnly),
"disabled": fmt.Sprintf("%t", s.Disabled),
}
for key, val := range s.Properties {
result["pro."+key] = val
@ -71,6 +75,8 @@ func MapToStore(data map[string]string) (store Store) {
URL: data["url"],
Username: data["username"],
Password: data["password"],
ReadOnly: data["readonly"] == "true",
Disabled: data["disabled"] == "true",
Kind: StoreKind{
Name: data["kind"],
URL: data["kind.url"],

View File

@ -133,9 +133,11 @@ var sampleStoreMap = map[string]string{
"kind.url": fooURL,
"kind": "test",
"description": "desc",
"disabled": "false",
"username": "user",
"password": "pass",
"pro.key": "val",
"readonly": "false",
}
var sampleStore = &Store{

View File

@ -8,6 +8,8 @@ stores:
url: localhost:4000
username: root
password: ""
readonly: false
disabled: false
properties:
database: test
plugins: