diff --git a/frontend/src/api/flow.js b/frontend/src/api/flow.js
index 46219722a..72acf0fd8 100644
--- a/frontend/src/api/flow.js
+++ b/frontend/src/api/flow.js
@@ -7,6 +7,12 @@ export const getFlowDetail = (flowId) => {
})
}
+export const getFlowDetailOrigin = (flowId) => {
+ return axios({
+ url: '/api/flow/' + flowId + "?is_origin=true"
+ })
+}
+
export const getFlowList = () => {
return axios({
url: '/api/flow'
diff --git a/frontend/src/store/inspector.js b/frontend/src/store/inspector.js
index 8177feb80..7b0360c16 100644
--- a/frontend/src/store/inspector.js
+++ b/frontend/src/store/inspector.js
@@ -1,5 +1,6 @@
import * as api from '@/api'
import { bus } from '@/eventbus'
+import {generateCurl} from '@/utils'
export default {
state: {
@@ -75,6 +76,10 @@ export default {
return
}
state.selectedFlowFilter = selectedFlowFilterName
+ },
+ generateAndCopyCurl(state, requestData){
+ let cmd = generateCurl(requestData)
+ bus.$emit('clipboard', cmd)
}
},
actions: {
@@ -188,6 +193,15 @@ export default {
.catch(error => {
bus.$emit('msg.error', 'Delete flow error: ' + error.data.message)
})
+ },
+ getFlowDetailForCmd ({state, commit}, flowId) {
+ api.getFlowDetailOrigin(flowId)
+ .then(response => {
+ commit('generateAndCopyCurl', response.data.data.request)
+ })
+ .catch(error => {
+ bus.$emit('msg.error', 'Get flow detail error: ' + error.data.message)
+ })
}
}
}
diff --git a/frontend/src/utils.js b/frontend/src/utils.js
index 496660be5..7235c4a7e 100644
--- a/frontend/src/utils.js
+++ b/frontend/src/utils.js
@@ -1,3 +1,5 @@
+import { bus } from '@/eventbus'
+
export const readablizeBytes = (bytes) => {
if(bytes===0){
return '0 B'
@@ -31,3 +33,64 @@ export const timestampToDatetime = (timeStamp) => {
let second = (dateObj.getSeconds() < 10 ? '0'+dateObj.getSeconds() : dateObj.getSeconds())
return month + '/' + date + ' ' + hour + ':' + minute + ':' + second
}
+
+export const generateCurl = (requestData) => {
+ let contentType = requestData['headers']['Content-Type'] || ''
+ let curl = ['curl ' + generateCurlUrl(requestData['url'])]
+ curl = curl.concat(generateCurlMethod(requestData['method']))
+ curl = curl.concat(generateCurlHeader(requestData['headers']))
+ curl = curl.concat(generateCurlData(requestData['data'], contentType))
+ return curl.join(' \\\n ')
+}
+
+function generateCurlUrl (url) {
+ return `-g \"${url}\"`
+}
+
+function generateCurlMethod (method) {
+ return ['-X '+method]
+}
+
+function generateCurlHeader (headers) {
+ let ignoreKey = [
+ 'Host',
+ 'Accept-Encoding'
+ ]
+ let headerStrList = []
+ for(let key in headers){
+ if(!ignoreKey.includes(key))
+ headerStrList.push(`-H \"${key}:${headers[key]}\"`)
+ }
+ return headerStrList
+}
+
+function generateCurlData (data, dataType) {
+ if(typeof data === 'undefined' || !data){
+ return []
+ }
+ let dataStrList = []
+ if(!dataType){
+ dataStrList.push(`--data-raw \'${generateJsonString(data)}\'`)
+ }else if(dataType.includes('application/json')){
+ dataStrList.push(`-d \'${generateJsonString(data)}\'`)
+ }else if(dataType.includes('application/x-www-form-urlencoded')){
+ dataStrList = Object.entries(data).map(([key, value]) => `-d \"${key}=${data[key]}\"`)
+ }else{
+ bus.$emit('msg.error', `Generate curl param -d failed: Lyrebird doesn't support ContentType of ${dataType}`)
+ }
+ return dataStrList
+}
+
+function generateJsonString (data) {
+ let res = ''
+ if(typeof data === 'string'){
+ return data.trim()
+ }else{
+ try{
+ res = JSON.stringify(data)
+ }catch(e){
+ bus.$emit('msg.error', `Generate curl param -d failed: Data type conversion failed`)
+ }
+ }
+ return res
+}
\ No newline at end of file
diff --git a/frontend/src/views/Main.vue b/frontend/src/views/Main.vue
index 5b2600618..73a3ac2c6 100644
--- a/frontend/src/views/Main.vue
+++ b/frontend/src/views/Main.vue
@@ -87,6 +87,7 @@ export default {
this.$bus.$off('msg.info', this.infoMessage)
this.$bus.$off('msg.error', this.errorMessage)
this.$bus.$off('msg.destroy', this.destroyMessage)
+ this.$bus.$off('clipboard', this.copyToClipboard)
},
created() {
this.$bus.$on('msg.success', this.successMessage)
@@ -94,6 +95,7 @@ export default {
this.$bus.$on('msg.info', this.infoMessage)
this.$bus.$on('msg.error', this.errorMessage)
this.$bus.$on('msg.destroy', this.destroyMessage)
+ this.$bus.$on('clipboard', this.copyToClipboard)
this.$io.on('statusBarUpdate', this.loadAllStatusList)
this.$io.on('datamanagerUpdate', this.loadDataMap)
this.$io.on('msgSuccess', this.successMessage)
@@ -198,6 +200,13 @@ export default {
destroyMessage() {
this.$Message.destroy()
},
+ copyToClipboard(text) {
+ this.$copyText(text).then(e => {
+ this.$bus.$emit('msg.success', 'Copy Success')
+ }, e => {
+ this.$bus.$emit('msg.error', 'Copy Failed')
+ })
+ }
},
}
diff --git a/frontend/src/views/inspector/FlowList.vue b/frontend/src/views/inspector/FlowList.vue
index f65136075..a2463f7a2 100644
--- a/frontend/src/views/inspector/FlowList.vue
+++ b/frontend/src/views/inspector/FlowList.vue
@@ -117,6 +117,27 @@
+
+
+
+
+
+ mdi-xml
+
+
+
+ Copy as cURL
+
+
+
@@ -132,7 +153,7 @@
- Copy
+ Copy Url
@@ -380,6 +401,9 @@ export default {
}
return row.response.mock
},
+ generateCurlUrl(item){
+ this.$store.dispatch('getFlowDetailForCmd', item.id)
+ },
onUrlCopy () {
this.$bus.$emit('msg.success', 'URL copied!')
},
@@ -493,7 +517,7 @@ export default {
display: inline-block;
word-break: keep-all;
max-width: 900px;
- width: calc(100% - 50px);
+ width: calc(100% - 100px);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
diff --git a/lyrebird/mock/blueprints/apis/flow.py b/lyrebird/mock/blueprints/apis/flow.py
index c8c649d44..4ded1b374 100644
--- a/lyrebird/mock/blueprints/apis/flow.py
+++ b/lyrebird/mock/blueprints/apis/flow.py
@@ -17,11 +17,15 @@ class Flow(Resource):
def get(self, id):
is_decode = request.args.get('is_decode', 'false').strip().lower() == 'true'
+ is_origin = request.args.get('is_origin', 'false').strip().lower() == 'true'
for item in context.application.cache.items():
if item['id'] == id:
# Import decoder for decoding the requested content
display_item = {}
- application.encoders_decoders.decoder_handler(item, output=display_item)
+ if is_origin:
+ display_item.update(item)
+ else:
+ application.encoders_decoders.decoder_handler(item, output=display_item)
if is_decode:
display_item['request'] = deepcopy(display_item['request'])
for key in ('url', 'path', 'query'):