Skip to content

Commit

Permalink
feature/core-keep-origin-request-data (#757)
Browse files Browse the repository at this point in the history
* Core support keep origin request data; Inspector add keep_origin_data switch

* update version

* Update code

* remove unused func

* recover config

* add case
  • Loading branch information
yumiguan authored May 25, 2023
1 parent 4bdf228 commit 0fc1944
Show file tree
Hide file tree
Showing 19 changed files with 383 additions and 63 deletions.
7 changes: 7 additions & 0 deletions e2e_tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ def test_json(lyrebird, mock_server):
assert r.text == hashlib.md5(mock_server.api_post.encode() + data.encode()).hexdigest()


def test_javascript(lyrebird, mock_server):
data = "(function(){\n var name = 'Lyrebird';\n })();"
headers = {"Content-Type": "application/javascript"}
r = requests.post(url=lyrebird.uri_mock + mock_server.api_post, data=data, headers=headers)
assert r.text == hashlib.md5(mock_server.api_post.encode() + data.encode()).hexdigest()


def test_text(lyrebird, mock_server):
data = "asdasdasd"
headers = {"Content-Type": "text/plain"}
Expand Down
9 changes: 9 additions & 0 deletions e2e_tests/test_flow_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ def test_flow_editor_json(lyrebird_with_args, mock_server):
assert r.text == hashlib.md5(mock_server.api_post.encode() + data.encode()).hexdigest()


def test_flow_editor_js(lyrebird_with_args, mock_server):
lyrebird_with_args.start(checker_path=flow_editor_path)

data = "console.log('hello world')"
headers = {"Content-Type": "application/javascript"}
r = requests.post(url=lyrebird_with_args.uri_mock + mock_server.api_post, data=data, headers=headers)
assert r.text == hashlib.md5(mock_server.api_post.encode() + data.encode()).hexdigest()


def test_flow_editor_text(lyrebird_with_args, mock_server):
lyrebird_with_args.start(checker_path=flow_editor_path)
data = "asdasdasd"
Expand Down
14 changes: 0 additions & 14 deletions frontend/src/api/inspector.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,5 @@
import axios from 'axios'

export const getDiffModeStatus = () => {
return axios({
url: '/api/diffmode'
})
}

export const setDiffModeStatus = (status) => {
return axios({
url: '/api/diffmode',
data: { status },
method: 'PUT'
})
}

export const getFlowFilters = () => {
return axios({
url: '/api/flowfilter'
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/store/inspector.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export default {
focusedFlowDetail: null,
originFlowList: [],
recordMode: '',
diffMode: 'normal',
isRequestKeepOriginData: false,
flowFilters: [],
selectedFlowFilter: {}
},
Expand Down Expand Up @@ -54,6 +56,12 @@ export default {
setRecordMode (state, recordMode) {
state.recordMode = recordMode
},
setDiffMode (state, diffMode) {
state.diffMode = diffMode
},
setIsRequestKeepOriginData (state, isRequestKeepOriginData) {
state.isRequestKeepOriginData = isRequestKeepOriginData
},
setFlowFilters (state, flowFilters) {
state.flowFilters = flowFilters
},
Expand Down
20 changes: 20 additions & 0 deletions frontend/src/store/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import * as api from '@/api'
import { bus } from '@/eventbus'

var configCommitMap = [
{'name': 'mock.mode', 'commit': 'setDiffMode'},
{'name': 'mock.data.showLabel', 'commit': 'setIsLabelDisplay'},
{'name': 'mock.data.tree.closeReload', 'commit': 'setIsCloseReloadWhenEnter'},
{'name': 'mock.request.keep_origin_data', 'commit': 'setIsRequestKeepOriginData'},
{'name': 'mock.data.tree.undeletableId', 'commit': 'concatTreeUndeletableId'},
{'name': 'mock.data.detail.stickyTopKey', 'commit': 'concatStickyTopKey'},
{'name': 'mock.data.detail.undeletableKey', 'commit': 'concatUndeletableKey'},
Expand Down Expand Up @@ -73,6 +75,24 @@ export default {
.catch(error => {
bus.$emit('msg.error', `Update config failed ${error.data.message}`)
})
},
commitAndupdateConfigByKey({ dispatch, commit }, { command, val }) {
let updateConfig = {}
for (const config of configCommitMap) {
if (config.commit == command) {
updateConfig[config.name] = val
commit(command, val)
break
}
}
api.updateConfigByKey(updateConfig)
.then(_ => {
dispatch('loadConfig')
bus.$emit('msg.success', `Update config success!`)
})
.catch(error => {
bus.$emit('msg.error', `Update config failed ${error.data.message}`)
})
}
}
}
114 changes: 71 additions & 43 deletions frontend/src/views/inspector/ButtonBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,30 +29,8 @@
<span>Clear</span>
</v-tooltip>

<v-divider vertical class="button-bar-divider border"/>
<b style="padding-right:5px">Diff Mode</b>
<v-switch
small
dense
inset
v-model="diffMode"
color="primary"
class="button-bar-diff-mode"
@change="changeDiffMode"
/>

<v-tooltip bottom>
<template v-slot:activator="{ on, attrs }">
<v-btn plain icon small v-bind="attrs" v-on="on">
<v-icon small size="18px" color="content">mdi-help-circle-outline</v-icon>
</v-btn>
</template>
<span>Get the proxy response while the request is mocked</span>
</v-tooltip>

<v-divider vertical class="button-bar-divider border"/>

<b style="padding-right:5px">Mock Group</b>
<b class="pr-1 pl-3">Mock Group</b>

<v-chip
label small outlined
Expand Down Expand Up @@ -124,6 +102,54 @@
@click:clear="clearInspectorSearch"
/>
</div>


<v-menu
left
bottom
offset-y
offset-overflow
:close-on-content-click="false"
style="position: absolute;"
>

<template v-slot:activator="{ on, attrs }">
<v-btn
icon
v-bind="attrs"
v-on="on"
class="ml-1"
title="Settings"
>
<v-icon size="18px" color="content">mdi-cog-outline</v-icon>
</v-btn>
</template>

<v-list dense>
<v-list-item>
<v-list-item-action>
<v-switch v-model="diffMode"/>
</v-list-item-action>

<v-list-item-content>
<v-list-item-title>Diff Mode</v-list-item-title>
<v-list-item-subtitle>Get the proxy response while the request is mocked</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>

<v-list-item>
<v-list-item-action>
<v-switch v-model="isRequestKeepOriginData"/>
</v-list-item-action>

<v-list-item-content>
<v-list-item-title>Origin request body</v-list-item-title>
<v-list-item-subtitle>Keep origin request body</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
</v-list>

</v-menu>
</div>

<MockDataSelector ref="searchModal" :showRoot="false">
Expand Down Expand Up @@ -158,7 +184,6 @@
<script>
import MockDataSelector from '@/components/SearchModal.vue'
import Icon from 'vue-svg-icon/Icon.vue'
import { getDiffModeStatus, setDiffModeStatus } from '@/api'
export default {
name: 'buttonBar',
Expand All @@ -168,15 +193,31 @@ export default {
},
data () {
return {
diffMode: false
}
},
mounted () {
this.loadDiffModeStatus()
},
computed: {
isRecordMode () {
return this.$store.state.inspector.recordMode === 'record'
diffMode: {
get () {
return this.$store.state.inspector.diffMode === 'multiple'
},
set (val) {
const mode = val ? 'multiple' : 'normal'
this.$store.commit('setDiffMode', mode)
this.$store.dispatch('updateConfigByKey', {
'mock.mode': mode
})
}
},
isRequestKeepOriginData: {
get () {
return this.$store.state.inspector.isRequestKeepOriginData
},
set (val) {
this.$store.dispatch('commitAndupdateConfigByKey', {
'command': 'setIsRequestKeepOriginData',
val
})
}
},
isEmptySelectedFlow () {
return this.$store.state.inspector.selectedFlows.length === 0
Expand Down Expand Up @@ -225,19 +266,6 @@ export default {
showMockDataSelector () {
this.$refs.searchModal.toggal()
},
loadDiffModeStatus () {
getDiffModeStatus()
.then(response => {
this.diffMode = response.data.diffmode === 'multiple'
})
.catch(error => {
this.$bus.$emit('msg.error', 'Load diff mode status failed: ' + error.data.message)
})
},
changeDiffMode (payload) {
const mode = payload ? 'multiple' : 'normal'
setDiffModeStatus(mode)
},
changeFlowFilter () {
this.$store.dispatch('loadFlowList')
},
Expand Down
7 changes: 7 additions & 0 deletions lyrebird/mock/handlers/handler_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ def _parse_request(self):
self.flow['client_address'] = self.client_address

self.flow['request'] = _request

if self.request.method in ['POST', 'PUT'] and application.config.get('mock.request.keep_origin_data'):
origin_data = DataHelper.origin2string(self.request)
self.flow['origin_request'] = {
'data': origin_data
}

context.application.cache.add(self.flow)

logger.debug(f'[On client request] {self.flow["request"]["url"]}')
Expand Down
25 changes: 25 additions & 0 deletions lyrebird/mock/handlers/http_data_helper/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,28 @@ def flow2origin(flow_obj, output=None, chain=None):
output.data = _data
else:
return _data

@staticmethod
def origin2string(origin_obj, output=None):
if not origin_obj:
return

_data = origin_obj.data
if not _data:
return

# Read raw headers, support the request from extra mock 9999 port
if 'Proxy-Raw-Headers' in origin_obj.headers:
_origin_headers = json.loads(origin_obj.headers['Proxy-Raw-Headers'])
raw_headers = CaseInsensitiveDict(_origin_headers)
else:
raw_headers = origin_obj.headers

for headers_key, func in origin2flow_handlers.items():
headers_val = raw_headers.get(headers_key, '')
_data = func.origin2string(headers_val, _data)

if output:
output['data'] = _data
else:
return _data
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ def origin2flow(content_encoding, request_data, chain=None):
_data = func.origin2flow(request_data)
logger.warning(f'Convert Content-Encoding: {content_encoding} data origin2flow failed! {e}')
finally:
chain.append(func)
if chain and isinstance(chain, list):
chain.append(func)

return _data

Expand All @@ -34,3 +35,14 @@ def flow2origin(content_encoding, flow_data):
_data = DefaultHandler.flow2origin(flow_data)
logger.warning(f'Convert Content-Encoding: {content_encoding} data flow2origin failed! {e}')
return _data

def origin2string(content_encoding, request_data):
func = _get_matched_action(content_encoding)
try:
_data = func.origin2string(request_data)
except Exception as e:
func = DefaultHandler
_data = func.origin2string(request_data)
logger.warning(f'Convert Content-Encoding: {content_encoding} data origin2flow failed! {e}')

return _data
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ def origin2flow(request_data):
@staticmethod
def flow2origin(flow_data):
return flow_data

@staticmethod
def origin2string(request_data):
return request_data
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ def origin2flow(request_data):
def flow2origin(flow_data):
_data = gzip.compress(flow_data)
return _data

@staticmethod
def origin2string(request_data):
_data = gzip.decompress(request_data)
return _data
13 changes: 12 additions & 1 deletion lyrebird/mock/handlers/http_data_helper/content_type/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ def origin2flow(content_type, request_data, chain=None):
_data = func.origin2flow(request_data)
logger.warning(f'Convert Content-Type: {content_type} data origin2flow failed! {e}')
finally:
chain.append(func)
if chain and isinstance(chain, list):
chain.append(func)

return _data

Expand All @@ -41,3 +42,13 @@ def flow2origin(content_type, flow_data):
_data = DefaultHandler.flow2origin(flow_data)
logger.warning(f'Convert Content-Type: {content_type} data flow2origin failed! {e}')
return _data

def origin2string(content_type, request_data):
func = _get_matched_action(content_type)
try:
_data = func.origin2string(request_data)
except Exception as e:
func = DefaultHandler
_data = func.origin2string(request_data)
logger.warning(f'Convert Content-Type: {content_type} data origin2flow failed! {e}')
return _data
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ def origin2flow(request_data):
def flow2origin(flow_data):
_data = binascii.a2b_base64(flow_data)
return _data

@staticmethod
def origin2string(request_data):
_data = binascii.b2a_base64(request_data).decode('utf-8')
return _data
6 changes: 5 additions & 1 deletion lyrebird/mock/handlers/http_data_helper/content_type/form.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ class FormHandler:

@staticmethod
def origin2flow(request_data):
params = parse_qs(request_data.decode('utf-8'))
params = parse_qs(request_data.decode('utf-8'), keep_blank_values=True)
_data = {k:v[0] for k,v in params.items()}
return _data

@staticmethod
def flow2origin(flow_data):
_data = urlencode(flow_data).encode()
return _data

@staticmethod
def origin2string(request_data):
return request_data.decode('utf-8')
Loading

0 comments on commit 0fc1944

Please sign in to comment.