Loading config/secrets.yaml +1 −1 Original line number Diff line number Diff line Loading @@ -3,7 +3,7 @@ meep-session: meep-oauth-github: client-id: "Ov23liZqkabhEidZnJgp" secret: "712dfc9374fe192af5833f0735a05b84f54d9a11" secret: "a412f4b61cc9b4134f917521db6565153b040761" meep-oauth-gitlab: client-id: "my-gitlab-client-id" Loading js-apps/frontend/src/js/app-constants.js +15 −0 Original line number Diff line number Diff line Loading @@ -127,3 +127,18 @@ export const ELEMENT_TYPE_MECSVC = 'MEC SERVICE'; export const ELEMENT_TYPE_UE_APP = 'UE APPLICATION'; export const ELEMENT_TYPE_EDGE_APP = 'EDGE APPLICATION'; export const ELEMENT_TYPE_CLOUD_APP = 'CLOUD APPLICATION'; // MEC Services configuration export const MEC_SERVICES = [ { prefix: 'meep-app-enablement', code: '011', name: 'App Enablement' }, { prefix: 'meep-rnis', code: '012', name: 'RNIS' }, { prefix: 'meep-loc-serv', code: '013', name: 'Location' }, { prefix: 'meep-tm', code: '015', name: 'Traffic Management' }, { prefix: 'meep-dai', code: '016', name: 'Device Application Interface' }, { prefix: 'meep-ams', code: '021', name: 'Application Mobility' }, { prefix: 'meep-wais', code: '028', name: 'WLAN Access Information' }, { prefix: 'meep-vis', code: '030', name: 'V2X Information' }, { prefix: 'meep-iot', code: '033', name: 'IoT API' }, { prefix: 'meep-federation', code: '040', name: 'Federation API' }, { prefix: 'meep-sss', code: '046', name: 'SSS API' } ]; js-apps/frontend/src/js/components/dialogs/detailed-data-dialog.js +2 −51 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ import { Grid, GridCell } from '@rmwc/grid'; import BasicDialog from './basic-dialog'; import { mapService } from '../../util/string-manipulation'; import { DLG_DETAILED_DATA } from '../../app-constants'; Loading Loading @@ -76,57 +77,7 @@ class DetailedDataDialog extends Component { this.classes = props.classes; } mapService(loggerName) { //loggerName is a combination of loggerName and mep name let prefixes = ['meep-loc-serv', 'meep-federation', 'meep-rnis', 'meep-dai', 'meep-wais', 'meep-app-enablement', 'meep-ams', 'meep-iot', 'meep-sss', 'meep-vis', 'meep-tm']; let prefixStd = ''; var prefix = ''; for (var i = 0; i < prefixes.length; i++) { if (loggerName.startsWith(prefixes[i])) { prefix = prefixes[i]; break; } } switch(prefix) { case 'meep-app-enablement': prefixStd = '011'; break; case 'meep-rnis': prefixStd = '012'; break; case 'meep-loc-serv': prefixStd = '013'; break; case 'meep-tm': prefixStd = '015'; break; case 'meep-dai': prefixStd = '016'; break; case 'meep-ams': prefixStd = '021'; break; case 'meep-wais': prefixStd = '028'; break; case 'meep-vis': prefixStd = '030'; break; case 'meep-iot': prefixStd = '033'; break; case 'meep-federation': prefixStd = '040'; break; case 'meep-sss': prefixStd = '046'; break; default: prefixStd = 'N/A'; } return loggerName.replace(prefix, prefixStd); } mapDirection(direction) { switch(direction) { Loading @@ -148,7 +99,7 @@ class DetailedDataDialog extends Component { return null; } var serviceId = this.mapService(singleData.loggerName); var serviceId = mapService(singleData.loggerName); var id = serviceId + '-' + singleData.id; var svcName = 'MEC' + serviceId; var reqType = this.mapDirection(singleData.direction); Loading js-apps/frontend/src/js/containers/sandbox/api-table.js +222 −57 Original line number Diff line number Diff line Loading @@ -13,13 +13,20 @@ import { connect } from 'react-redux'; import React, { Component } from 'react'; import { withStyles } from '@material-ui/core/styles'; import { getSorting } from '../../util/table-utils'; import { TextField } from '@rmwc/textfield'; import { Select } from '@rmwc/select'; import { Checkbox } from '@rmwc/checkbox'; import { Button } from '@rmwc/button'; import { sboxChangeApiTable } from '../../state/sbox'; import { mapService } from '../../util/string-manipulation'; import { uiSandboxChangeApiDetailedData, uiChangeCurrentDialog } from '../../state/ui'; import { DIALOG_DETAILED_DATA, DEFAULT_NO_NETWORK_FILE_SELECTED DEFAULT_NO_NETWORK_FILE_SELECTED, MEC_SERVICES } from '../../app-constants'; const tableStyles = () => ({ Loading Loading @@ -56,9 +63,75 @@ const tableStyles = () => ({ paddingRight: 0, 'font-family': 'Rubik', whiteSpace: 'nowrap' }, toolbarContainer: { display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', padding: '8px 15px', borderBottom: '1px solid #d9e2ec', backgroundColor: '#ecf3f9', gap: '12px' }, filterGroup: { display: 'flex', flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', gap: '12px' }, actionGroup: { display: 'flex', flexDirection: 'row', alignItems: 'center', gap: '12px' }, filterField: { minWidth: '160px', width: '160px' }, searchField: { minWidth: '180px', width: '180px' }, checkboxLabel: { display: 'flex', alignItems: 'center', fontFamily: 'Rubik', fontSize: '0.9rem', marginLeft: '5px' }, button: { textTransform: 'none', fontFamily: 'Rubik' }, clearButton: { textTransform: 'none', fontFamily: 'Rubik', color: '#d32f2f', borderColor: '#d32f2f' } }); const serviceOptions = [ { label: 'All Services', value: 'ALL' }, ...MEC_SERVICES.map(svc => ({ label: `${svc.code} - ${svc.name}`, value: svc.code })) ]; const methodOptions = [ { label: 'All Methods', value: 'ALL' }, { label: 'GET', value: 'GET' }, { label: 'POST', value: 'POST' }, { label: 'PUT', value: 'PUT' }, { label: 'DELETE', value: 'DELETE' }, { label: 'PATCH', value: 'PATCH' } ]; const tableColumnData = [ { id: 'id', Loading Loading @@ -102,6 +175,27 @@ class ApiTable extends Component { constructor(props) { super(props); this.classes = props.classes; this.state = { searchTerm: '', serviceFilter: 'ALL', methodFilter: 'ALL', errorsOnly: false }; } exportLogs(filteredData) { if (!filteredData || !filteredData.length) { return; } const jsonString = `data:text/json;charset=utf-8,${encodeURIComponent( JSON.stringify(filteredData, null, 2) )}`; const downloadAnchor = document.createElement('a'); downloadAnchor.setAttribute('href', jsonString); downloadAnchor.setAttribute('download', `api_logs_${new Date().toISOString()}.json`); document.body.appendChild(downloadAnchor); downloadAnchor.click(); downloadAnchor.remove(); } onClick(event, row) { Loading @@ -109,57 +203,7 @@ class ApiTable extends Component { this.props.changeCurrentDialog(DIALOG_DETAILED_DATA); } mapService(loggerName) { //loggerName is a combination of loggerName and mep name let prefixes = ['meep-loc-serv', 'meep-rnis', 'meep-federation', 'meep-dai', 'meep-wais', 'meep-app-enablement', 'meep-ams', 'meep-vis', 'meep-iot', 'meep-sss', 'meep-tm']; let prefixStd = ''; var prefix = ''; for (var i = 0; i < prefixes.length; i++) { if (loggerName.startsWith(prefixes[i])) { prefix = prefixes[i]; break; } } switch(prefix) { case 'meep-loc-serv': prefixStd = '013'; break; case 'meep-rnis': prefixStd = '012'; break; case 'meep-wais': prefixStd = '028'; break; case 'meep-app-enablement': prefixStd = '011'; break; case 'meep-ams': prefixStd = '021'; break; case 'meep-tm': prefixStd = '015'; break; case 'meep-dai': prefixStd = '016'; break; case 'meep-vis': prefixStd = '030'; break; case 'meep-iot': prefixStd = '033'; break; case 'meep-federation': prefixStd = '040'; break; case 'meep-sss': prefixStd = '046'; break; default: prefixStd = 'N/A'; } return loggerName.replace(prefix, prefixStd); } mapDirection(direction) { switch(direction) { Loading @@ -172,6 +216,27 @@ class ApiTable extends Component { } } formatTime(timeStr) { if (!timeStr) { return ''; } try { const d = new Date(timeStr); if (isNaN(d.getTime())) { return timeStr; } const year = d.getFullYear(); const month = String(d.getMonth() + 1).padStart(2, '0'); const day = String(d.getDate()).padStart(2, '0'); const hours = String(d.getHours()).padStart(2, '0'); const minutes = String(d.getMinutes()).padStart(2, '0'); const seconds = String(d.getSeconds()).padStart(2, '0'); return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; } catch (e) { return timeStr; } } render() { const classes = this.classes; var data = null; Loading @@ -182,16 +247,115 @@ class ApiTable extends Component { const order = 'desc'; const orderBy = 'time'; // Apply filters client-side let filteredData = data || []; if (this.props.networkFileSelected === DEFAULT_NO_NETWORK_FILE_SELECTED) { filteredData = []; } if (filteredData.length > 0) { const { searchTerm, serviceFilter, methodFilter, errorsOnly } = this.state; filteredData = filteredData.filter(row => { // 1. Search term filter (endpoint) if (searchTerm) { const endpointLower = (row.endpoint || '').toLowerCase(); if (!endpointLower.includes(searchTerm.toLowerCase())) { return false; } } // 2. Service Filter if (serviceFilter !== 'ALL') { const serviceMapped = mapService(row.loggerName); if (!serviceMapped.startsWith(serviceFilter)) { return false; } } // 3. Method Filter if (methodFilter !== 'ALL') { if ((row.method || '') !== methodFilter) { return false; } } // 4. Errors Only if (errorsOnly) { const code = parseInt(row.respCode, 10); if (isNaN(code) || code < 400) { return false; } } return true; }); } const maxRows = 12; var emptyRows = 0; if (!data || !data.length) { if (!filteredData || !filteredData.length) { emptyRows = maxRows; } else { emptyRows = Math.max(0, maxRows - data.length); emptyRows = Math.max(0, maxRows - filteredData.length); } return ( <Paper className={classes.root}> {/* API Console Toolbar */} <div className={classes.toolbarContainer}> <div className={classes.filterGroup}> <TextField outlined label="Search endpoint..." value={this.state.searchTerm} onChange={e => this.setState({ searchTerm: e.target.value })} className={classes.searchField} /> <Select outlined label="Service" value={this.state.serviceFilter} options={serviceOptions} onChange={e => this.setState({ serviceFilter: e.target.value })} className={classes.filterField} /> <Select outlined label="Method" value={this.state.methodFilter} options={methodOptions} onChange={e => this.setState({ methodFilter: e.target.value })} className={classes.filterField} /> <Checkbox checked={this.state.errorsOnly} onChange={e => this.setState({ errorsOnly: e.target.checked })} className={classes.checkboxLabel} > Errors Only </Checkbox> </div> <div className={classes.actionGroup}> <Button outlined className={classes.button} onClick={() => this.exportLogs(filteredData)} disabled={!filteredData.length} > Export </Button> <Button outlined className={classes.clearButton} onClick={() => this.props.clearApiTable()} disabled={!data || !data.length} > Clear </Button> </div> </div> <div className={classes.tableWrapper}> <Table className={classes.table} Loading @@ -218,7 +382,7 @@ class ApiTable extends Component { </TableRow> </TableHead> {emptyRows < maxRows && <TableBody className={classes.tableBody} title="Click to get detailed info"> {data {filteredData .sort(getSorting(order, orderBy)) .map((row) => { return ( Loading @@ -230,7 +394,7 @@ class ApiTable extends Component { key={row.time} > <TableCell className={classes.tableBodyCellNoWrap} scope="row"> {this.mapService(row.loggerName) + '-' + row.id} {mapService(row.loggerName) + '-' + row.id} </TableCell> <TableCell className={classes.tableBodyCell} padding='none'> {row.respCode} Loading @@ -245,7 +409,7 @@ class ApiTable extends Component { {row.endpoint} </TableCell> <TableCell className={classes.tableBodyCell} padding='none'> {row.time} {this.formatTime(row.time)} </TableCell> </TableRow> ); Loading Loading @@ -287,7 +451,8 @@ const mapStateToProps = state => { const mapDispatchToProps = dispatch => { return { changeDetailedData: row => dispatch(uiSandboxChangeApiDetailedData(row)), changeCurrentDialog: type => dispatch(uiChangeCurrentDialog(type)) changeCurrentDialog: type => dispatch(uiChangeCurrentDialog(type)), clearApiTable: () => dispatch(sboxChangeApiTable({ values: [] })) }; }; Loading js-apps/frontend/src/js/util/string-manipulation.js +13 −0 Original line number Diff line number Diff line Loading @@ -2,6 +2,8 @@ * Copyright (c) 2022 ETSI. All rights reserved. */ import { MEC_SERVICES } from '../app-constants'; export const firstLetterUpper = str => { if (!str) { return ''; Loading @@ -18,3 +20,14 @@ export const camelCasePrefix = prefix => { array[0] = f.charAt(0).toLowerCase() + f.slice(1); return array.join(''); }; export const mapService = loggerName => { if (!loggerName) { return ''; } const svc = MEC_SERVICES.find(s => loggerName.startsWith(s.prefix)); if (svc) { return loggerName.replace(svc.prefix, svc.code); } return 'N/A' + loggerName; }; Loading
config/secrets.yaml +1 −1 Original line number Diff line number Diff line Loading @@ -3,7 +3,7 @@ meep-session: meep-oauth-github: client-id: "Ov23liZqkabhEidZnJgp" secret: "712dfc9374fe192af5833f0735a05b84f54d9a11" secret: "a412f4b61cc9b4134f917521db6565153b040761" meep-oauth-gitlab: client-id: "my-gitlab-client-id" Loading
js-apps/frontend/src/js/app-constants.js +15 −0 Original line number Diff line number Diff line Loading @@ -127,3 +127,18 @@ export const ELEMENT_TYPE_MECSVC = 'MEC SERVICE'; export const ELEMENT_TYPE_UE_APP = 'UE APPLICATION'; export const ELEMENT_TYPE_EDGE_APP = 'EDGE APPLICATION'; export const ELEMENT_TYPE_CLOUD_APP = 'CLOUD APPLICATION'; // MEC Services configuration export const MEC_SERVICES = [ { prefix: 'meep-app-enablement', code: '011', name: 'App Enablement' }, { prefix: 'meep-rnis', code: '012', name: 'RNIS' }, { prefix: 'meep-loc-serv', code: '013', name: 'Location' }, { prefix: 'meep-tm', code: '015', name: 'Traffic Management' }, { prefix: 'meep-dai', code: '016', name: 'Device Application Interface' }, { prefix: 'meep-ams', code: '021', name: 'Application Mobility' }, { prefix: 'meep-wais', code: '028', name: 'WLAN Access Information' }, { prefix: 'meep-vis', code: '030', name: 'V2X Information' }, { prefix: 'meep-iot', code: '033', name: 'IoT API' }, { prefix: 'meep-federation', code: '040', name: 'Federation API' }, { prefix: 'meep-sss', code: '046', name: 'SSS API' } ];
js-apps/frontend/src/js/components/dialogs/detailed-data-dialog.js +2 −51 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ import { Grid, GridCell } from '@rmwc/grid'; import BasicDialog from './basic-dialog'; import { mapService } from '../../util/string-manipulation'; import { DLG_DETAILED_DATA } from '../../app-constants'; Loading Loading @@ -76,57 +77,7 @@ class DetailedDataDialog extends Component { this.classes = props.classes; } mapService(loggerName) { //loggerName is a combination of loggerName and mep name let prefixes = ['meep-loc-serv', 'meep-federation', 'meep-rnis', 'meep-dai', 'meep-wais', 'meep-app-enablement', 'meep-ams', 'meep-iot', 'meep-sss', 'meep-vis', 'meep-tm']; let prefixStd = ''; var prefix = ''; for (var i = 0; i < prefixes.length; i++) { if (loggerName.startsWith(prefixes[i])) { prefix = prefixes[i]; break; } } switch(prefix) { case 'meep-app-enablement': prefixStd = '011'; break; case 'meep-rnis': prefixStd = '012'; break; case 'meep-loc-serv': prefixStd = '013'; break; case 'meep-tm': prefixStd = '015'; break; case 'meep-dai': prefixStd = '016'; break; case 'meep-ams': prefixStd = '021'; break; case 'meep-wais': prefixStd = '028'; break; case 'meep-vis': prefixStd = '030'; break; case 'meep-iot': prefixStd = '033'; break; case 'meep-federation': prefixStd = '040'; break; case 'meep-sss': prefixStd = '046'; break; default: prefixStd = 'N/A'; } return loggerName.replace(prefix, prefixStd); } mapDirection(direction) { switch(direction) { Loading @@ -148,7 +99,7 @@ class DetailedDataDialog extends Component { return null; } var serviceId = this.mapService(singleData.loggerName); var serviceId = mapService(singleData.loggerName); var id = serviceId + '-' + singleData.id; var svcName = 'MEC' + serviceId; var reqType = this.mapDirection(singleData.direction); Loading
js-apps/frontend/src/js/containers/sandbox/api-table.js +222 −57 Original line number Diff line number Diff line Loading @@ -13,13 +13,20 @@ import { connect } from 'react-redux'; import React, { Component } from 'react'; import { withStyles } from '@material-ui/core/styles'; import { getSorting } from '../../util/table-utils'; import { TextField } from '@rmwc/textfield'; import { Select } from '@rmwc/select'; import { Checkbox } from '@rmwc/checkbox'; import { Button } from '@rmwc/button'; import { sboxChangeApiTable } from '../../state/sbox'; import { mapService } from '../../util/string-manipulation'; import { uiSandboxChangeApiDetailedData, uiChangeCurrentDialog } from '../../state/ui'; import { DIALOG_DETAILED_DATA, DEFAULT_NO_NETWORK_FILE_SELECTED DEFAULT_NO_NETWORK_FILE_SELECTED, MEC_SERVICES } from '../../app-constants'; const tableStyles = () => ({ Loading Loading @@ -56,9 +63,75 @@ const tableStyles = () => ({ paddingRight: 0, 'font-family': 'Rubik', whiteSpace: 'nowrap' }, toolbarContainer: { display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', padding: '8px 15px', borderBottom: '1px solid #d9e2ec', backgroundColor: '#ecf3f9', gap: '12px' }, filterGroup: { display: 'flex', flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', gap: '12px' }, actionGroup: { display: 'flex', flexDirection: 'row', alignItems: 'center', gap: '12px' }, filterField: { minWidth: '160px', width: '160px' }, searchField: { minWidth: '180px', width: '180px' }, checkboxLabel: { display: 'flex', alignItems: 'center', fontFamily: 'Rubik', fontSize: '0.9rem', marginLeft: '5px' }, button: { textTransform: 'none', fontFamily: 'Rubik' }, clearButton: { textTransform: 'none', fontFamily: 'Rubik', color: '#d32f2f', borderColor: '#d32f2f' } }); const serviceOptions = [ { label: 'All Services', value: 'ALL' }, ...MEC_SERVICES.map(svc => ({ label: `${svc.code} - ${svc.name}`, value: svc.code })) ]; const methodOptions = [ { label: 'All Methods', value: 'ALL' }, { label: 'GET', value: 'GET' }, { label: 'POST', value: 'POST' }, { label: 'PUT', value: 'PUT' }, { label: 'DELETE', value: 'DELETE' }, { label: 'PATCH', value: 'PATCH' } ]; const tableColumnData = [ { id: 'id', Loading Loading @@ -102,6 +175,27 @@ class ApiTable extends Component { constructor(props) { super(props); this.classes = props.classes; this.state = { searchTerm: '', serviceFilter: 'ALL', methodFilter: 'ALL', errorsOnly: false }; } exportLogs(filteredData) { if (!filteredData || !filteredData.length) { return; } const jsonString = `data:text/json;charset=utf-8,${encodeURIComponent( JSON.stringify(filteredData, null, 2) )}`; const downloadAnchor = document.createElement('a'); downloadAnchor.setAttribute('href', jsonString); downloadAnchor.setAttribute('download', `api_logs_${new Date().toISOString()}.json`); document.body.appendChild(downloadAnchor); downloadAnchor.click(); downloadAnchor.remove(); } onClick(event, row) { Loading @@ -109,57 +203,7 @@ class ApiTable extends Component { this.props.changeCurrentDialog(DIALOG_DETAILED_DATA); } mapService(loggerName) { //loggerName is a combination of loggerName and mep name let prefixes = ['meep-loc-serv', 'meep-rnis', 'meep-federation', 'meep-dai', 'meep-wais', 'meep-app-enablement', 'meep-ams', 'meep-vis', 'meep-iot', 'meep-sss', 'meep-tm']; let prefixStd = ''; var prefix = ''; for (var i = 0; i < prefixes.length; i++) { if (loggerName.startsWith(prefixes[i])) { prefix = prefixes[i]; break; } } switch(prefix) { case 'meep-loc-serv': prefixStd = '013'; break; case 'meep-rnis': prefixStd = '012'; break; case 'meep-wais': prefixStd = '028'; break; case 'meep-app-enablement': prefixStd = '011'; break; case 'meep-ams': prefixStd = '021'; break; case 'meep-tm': prefixStd = '015'; break; case 'meep-dai': prefixStd = '016'; break; case 'meep-vis': prefixStd = '030'; break; case 'meep-iot': prefixStd = '033'; break; case 'meep-federation': prefixStd = '040'; break; case 'meep-sss': prefixStd = '046'; break; default: prefixStd = 'N/A'; } return loggerName.replace(prefix, prefixStd); } mapDirection(direction) { switch(direction) { Loading @@ -172,6 +216,27 @@ class ApiTable extends Component { } } formatTime(timeStr) { if (!timeStr) { return ''; } try { const d = new Date(timeStr); if (isNaN(d.getTime())) { return timeStr; } const year = d.getFullYear(); const month = String(d.getMonth() + 1).padStart(2, '0'); const day = String(d.getDate()).padStart(2, '0'); const hours = String(d.getHours()).padStart(2, '0'); const minutes = String(d.getMinutes()).padStart(2, '0'); const seconds = String(d.getSeconds()).padStart(2, '0'); return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; } catch (e) { return timeStr; } } render() { const classes = this.classes; var data = null; Loading @@ -182,16 +247,115 @@ class ApiTable extends Component { const order = 'desc'; const orderBy = 'time'; // Apply filters client-side let filteredData = data || []; if (this.props.networkFileSelected === DEFAULT_NO_NETWORK_FILE_SELECTED) { filteredData = []; } if (filteredData.length > 0) { const { searchTerm, serviceFilter, methodFilter, errorsOnly } = this.state; filteredData = filteredData.filter(row => { // 1. Search term filter (endpoint) if (searchTerm) { const endpointLower = (row.endpoint || '').toLowerCase(); if (!endpointLower.includes(searchTerm.toLowerCase())) { return false; } } // 2. Service Filter if (serviceFilter !== 'ALL') { const serviceMapped = mapService(row.loggerName); if (!serviceMapped.startsWith(serviceFilter)) { return false; } } // 3. Method Filter if (methodFilter !== 'ALL') { if ((row.method || '') !== methodFilter) { return false; } } // 4. Errors Only if (errorsOnly) { const code = parseInt(row.respCode, 10); if (isNaN(code) || code < 400) { return false; } } return true; }); } const maxRows = 12; var emptyRows = 0; if (!data || !data.length) { if (!filteredData || !filteredData.length) { emptyRows = maxRows; } else { emptyRows = Math.max(0, maxRows - data.length); emptyRows = Math.max(0, maxRows - filteredData.length); } return ( <Paper className={classes.root}> {/* API Console Toolbar */} <div className={classes.toolbarContainer}> <div className={classes.filterGroup}> <TextField outlined label="Search endpoint..." value={this.state.searchTerm} onChange={e => this.setState({ searchTerm: e.target.value })} className={classes.searchField} /> <Select outlined label="Service" value={this.state.serviceFilter} options={serviceOptions} onChange={e => this.setState({ serviceFilter: e.target.value })} className={classes.filterField} /> <Select outlined label="Method" value={this.state.methodFilter} options={methodOptions} onChange={e => this.setState({ methodFilter: e.target.value })} className={classes.filterField} /> <Checkbox checked={this.state.errorsOnly} onChange={e => this.setState({ errorsOnly: e.target.checked })} className={classes.checkboxLabel} > Errors Only </Checkbox> </div> <div className={classes.actionGroup}> <Button outlined className={classes.button} onClick={() => this.exportLogs(filteredData)} disabled={!filteredData.length} > Export </Button> <Button outlined className={classes.clearButton} onClick={() => this.props.clearApiTable()} disabled={!data || !data.length} > Clear </Button> </div> </div> <div className={classes.tableWrapper}> <Table className={classes.table} Loading @@ -218,7 +382,7 @@ class ApiTable extends Component { </TableRow> </TableHead> {emptyRows < maxRows && <TableBody className={classes.tableBody} title="Click to get detailed info"> {data {filteredData .sort(getSorting(order, orderBy)) .map((row) => { return ( Loading @@ -230,7 +394,7 @@ class ApiTable extends Component { key={row.time} > <TableCell className={classes.tableBodyCellNoWrap} scope="row"> {this.mapService(row.loggerName) + '-' + row.id} {mapService(row.loggerName) + '-' + row.id} </TableCell> <TableCell className={classes.tableBodyCell} padding='none'> {row.respCode} Loading @@ -245,7 +409,7 @@ class ApiTable extends Component { {row.endpoint} </TableCell> <TableCell className={classes.tableBodyCell} padding='none'> {row.time} {this.formatTime(row.time)} </TableCell> </TableRow> ); Loading Loading @@ -287,7 +451,8 @@ const mapStateToProps = state => { const mapDispatchToProps = dispatch => { return { changeDetailedData: row => dispatch(uiSandboxChangeApiDetailedData(row)), changeCurrentDialog: type => dispatch(uiChangeCurrentDialog(type)) changeCurrentDialog: type => dispatch(uiChangeCurrentDialog(type)), clearApiTable: () => dispatch(sboxChangeApiTable({ values: [] })) }; }; Loading
js-apps/frontend/src/js/util/string-manipulation.js +13 −0 Original line number Diff line number Diff line Loading @@ -2,6 +2,8 @@ * Copyright (c) 2022 ETSI. All rights reserved. */ import { MEC_SERVICES } from '../app-constants'; export const firstLetterUpper = str => { if (!str) { return ''; Loading @@ -18,3 +20,14 @@ export const camelCasePrefix = prefix => { array[0] = f.charAt(0).toLowerCase() + f.slice(1); return array.join(''); }; export const mapService = loggerName => { if (!loggerName) { return ''; } const svc = MEC_SERVICES.find(s => loggerName.startsWith(s.prefix)); if (svc) { return loggerName.replace(svc.prefix, svc.code); } return 'N/A' + loggerName; };