import yaml from 'js-yaml' ; import { keys , reduce , constant , filter , tap , identity , isArray , isFunction , isPlainObject , isRegExp , map , mapValues , mergeWith , pick , pipe , toPairs , values , __ , isObject } from 'lodash/fp' ; import { mock } from 'mockjs' ; import genUUID from 'uuid' ; import { ApiRecord , API_DATA_TYPE , Snippet , SnippetContent , SOCKET_MSG_TAG_API } from '../../lib/interface' ; import { Expression , ExpSchema } from '../../lib/exp-yaml' ; import ss from '../socket-server' ; import configManager from './config-manager' ; const snippetsFn = { } ; const snippetsMeta = { } ; let enableSnippet = true ; configManager . on ( 'afterConfigInit' , ( ) => { Object . entries ( configManager . get ( configManager . key . SNIPPET ) || { } ) . forEach ( ( [ key , val ] ) => { snippetsMeta [ key ] = val ; snippetsFn [ key ] = parseSnippetContent ( val . content ) ; } ) ; } ) ; function getSnippetMetaList ( ) { return yaml . dump ( values ( snippetsMeta ) , { schema : ExpSchema } ) ; } ss . on ( SOCKET_MSG_TAG_API . SP_GET , ( cb ) => { cb ( getSnippetMetaList ( ) ) ; } ) ; ss . on ( SOCKET_MSG_TAG_API . SP_SAVE , ( { id , code } , cb ) => { const { name , content , when } = yaml . load ( code , { schema : ExpSchema } ) ; const spId = id || genUUID ( ) ; snippetsMeta [ spId ] = Object . assign ( { } , snippetsMeta [ spId ] , { id : spId , name , when , content } ) ; configManager . emit ( 'update' , configManager . key . SNIPPET , snippetsMeta ) ; snippetsFn [ spId ] = parseSnippetContent ( content ) ; ss . broadcast ( SOCKET_MSG_TAG_API . SP_UPDATE , getSnippetMetaList ( ) ) ; } ) ; ss . on ( SOCKET_MSG_TAG_API . SP_DELETE , ( id ) => { delete snippetsFn [ id ] ; delete snippetsMeta [ id ] ; configManager . emit ( 'update' , configManager . key . SNIPPET , snippetsMeta ) ; ss . broadcast ( SOCKET_MSG_TAG_API . SP_UPDATE , getSnippetMetaList ( ) ) ; } ) ; ss . on ( SOCKET_MSG_TAG_API . SP_MAIN_ENABLED , ( cb ) => { cb ( enableSnippet ) ; } ) ; ss . on ( SOCKET_MSG_TAG_API . SP_SET_MAIN_ENABLED , ( val ) => { enableSnippet = ! ! val ; ss . broadcast ( SOCKET_MSG_TAG_API . SP_SET_MAIN_ENABLED , enableSnippet ) ; } ) ; ss . on ( SOCKET_MSG_TAG_API . SP_UPDAT_SINGLE_ENABLED , ( id , enableStatus ) => { snippetsMeta [ id ] . enabled = enableStatus ; configManager . emit ( 'update' , configManager . key . SNIPPET , snippetsMeta ) ; ss . broadcast ( SOCKET_MSG_TAG_API . SP_UPDATE , getSnippetMetaList ( ) ) ; } ) ; export enum PARSE_STRATEGY { FIXED = 'fixed' , MOCKJS = 'mockjs' , SNIPPET = 'snippet' } function parseStrategy ( { strategy = 'fixed' , value , key = null } ) { switch ( strategy ) { case PARSE_STRATEGY . FIXED : return constant ( value ) ; case PARSE_STRATEGY . MOCKJS : if ( key ) { return ( ) => values ( mock ( { [ key ] : value } ) ) [ 0 ] ; } return constant ( mock ( value ) ) ; case PARSE_STRATEGY . SNIPPET : const sId = value . split ( '|' ) . slice ( - 1 ) [ 0 ] ; const parsed = snippetsFn [ sId ] ; if ( parsed ) return parsed ; const source = configManager . get ( configManager . key . SNIPPET ) [ sId ] ; if ( ! source ) throw new Error ( `[snippet解析错误]找不到依赖的snippet：${ value }` ) ; const ps = parseSnippetContent ( source . content ) ; snippetsFn [ sId ] = ps ; return ps ; } return identity ; } function parse ( snippet ) { const fixedRegx = new RegExp ( `^\\$${ PARSE_STRATEGY . FIXED }\\s+` ) ; const mockjsRegx = new RegExp ( `^\\$${ PARSE_STRATEGY . MOCKJS }\\s+` ) ; const snippetRegx = new RegExp ( `^\\$${ PARSE_STRATEGY . SNIPPET }\\s+` ) ; const expSnippetRegx = new RegExp ( `^\.{3}\\$${ PARSE_STRATEGY . SNIPPET }$` ) ; if ( snippet instanceof Expression ) { return snippet . fn ; } else if ( isArray ( snippet ) ) { let rs = [ ] ; for ( let s of snippet ) { if ( isPlainObject ( s ) && keys ( s ) . length === 1 && expSnippetRegx . test ( keys ( s ) [ 0 ] ) ) { rs . push ( ... values ( parse ( s ) ) ) ; } else { rs . push ( parse ( s ) ) ; } } return rs ; } else if ( isFunction ( snippet ) ) { return snippet ; } else if ( isObject ( snippet ) ) { return pipe ( toPairs , map ( ( [ key , value ] ) => { if ( fixedRegx . test ( key ) ) { return [ key . replace ( fixedRegx , '' ) , parseStrategy ( { strategy : PARSE_STRATEGY . FIXED , value } ) ] ; } else if ( mockjsRegx . test ( key ) ) { return [ key . replace ( mockjsRegx , '' ) . replace ( /\|.+$/ , '' ) , parseStrategy ( { strategy : PARSE_STRATEGY . MOCKJS , key : key . replace ( mockjsRegx , '' ) , value } ) ] ; } else if ( snippetRegx . test ( key ) ) { return [ key . replace ( snippetRegx , '' ) , parseStrategy ( { strategy : PARSE_STRATEGY . SNIPPET , value } ) ] ; } else if ( expSnippetRegx . test ( key ) ) { return [ expSnippetRegx . source , parseStrategy ( { strategy : PARSE_STRATEGY . SNIPPET , value } ) ( null ) ] ; } return [ key , parse ( value ) ] ; } ) , reduce ( ( sum , [ k , v ] ) => k === expSnippetRegx . source ? ( { ... sum , ... v } ) : ( { ... sum , [ k ] : v } ) , { } ) ) ( snippet ) ; } return parseStrategy ( { value : snippet } ) ; } param remainder function expandSnippet ( remainder ) { if ( isFunction ( remainder ) ) return remainder ( ) ; if ( isArray ( remainder ) ) return map ( expandSnippet ) ( remainder ) ; if ( isPlainObject ( remainder ) ) return mapValues ( expandSnippet ) ( remainder ) ; return remainder ; } param snippet return export function parseSnippetContent ( snippet ) { const snippeter = parse ( snippet ) ; return ( data ) : any => { if ( isFunction ( snippeter ) ) return snippeter ( data ) ; if ( isPlainObject ( data ) && isPlainObject ( snippeter ) ) { const rs = mergeWith ( ( subOriginVal , subSnippetVal ) => { if ( isFunction ( subSnippetVal ) ) { return subSnippetVal ( subOriginVal ) ; } return undefined ; } , data , snippeter ) ; return rs ; } return expandSnippet ( snippeter ) ; } ; } param id return export function getSnippetFn ( id ) { if ( enableSnippet ) return snippetsFn [ id ] ; return identity ; } param record export function matchedSnippetFns ( record ) { if ( ! enableSnippet ) return [ ] ; const { req , resp , parsedUrl } = record ; const recordMeta = `${ resp ? API_DATA_TYPE . RESPONSE : API_DATA_TYPE . REQUEST }#${ req . method }#${ parsedUrl . href }` ; return pipe ( values , filter ( ( { enabled , when } ) => ( enabled && when && ( isRegExp ( when ) ? when . test ( recordMeta ) : when ( record ) ) ) ) , map ( 'id' ) , pick ( __ , snippetsFn ) , values ) ( snippetsMeta ) ; }