Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AEMO Status and Outages #245

Merged
merged 2 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/components/Page.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import AppBar from '@material-ui/core/AppBar'
import Tabs from '@material-ui/core/Tabs'
import Tab from '@material-ui/core/Tab'
import DiscoveryInfo from './data/discovery/DiscoveryInfo'
import AEMODiscoveryInfo from './data/discovery/AEMODiscoveryInfo'

const useStyles = makeStyles(theme => ({
hidden: {
Expand All @@ -37,6 +38,7 @@ function Page() {
<Tab label="Banking" />
<Tab label="Energy" />
<Tab label="Status and Outages" />
<Tab label="AEMO - Status and Outages" />
</Tabs>
</AppBar>
<div className={value === 0 ? '' : classes.hidden}>
Expand All @@ -50,6 +52,9 @@ function Page() {
<div className={value === 2 ? '' : classes.hidden}>
<DiscoveryInfo/>
</div>
<div className={value === 3 ? '' : classes.hidden}>
<AEMODiscoveryInfo/>
</div>
</Container>
);
}
Expand Down
100 changes: 100 additions & 0 deletions src/components/data/discovery/AEMODiscoveryInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React from 'react'
import Accordion from '@material-ui/core/Accordion'
import AccordionSummary from '@material-ui/core/AccordionSummary'
import AccordionActions from '@material-ui/core/AccordionActions'
import ExpandMoreIcon from '@material-ui/icons/ExpandMore'
import SubjectIcon from '@material-ui/icons/Subject'
import Typography from '@material-ui/core/Typography'
import Divider from '@material-ui/core/Divider'
import RefreshIcon from '@material-ui/icons/Refresh'
import Fab from '@material-ui/core/Fab'
import Tooltip from '@material-ui/core/Tooltip'
import { makeStyles } from '@material-ui/core/styles'
import { fade } from '@material-ui/core/styles/colorManipulator'
import StatusOutages from './StatusOutages'
import { connect } from 'react-redux'
import { retrieveStatus, retrieveOutages } from '../../../store/aemo_discovery'

const useStyles = makeStyles(theme => ({
container: {
marginLeft: theme.typography.pxToRem(20),
marginRight: theme.typography.pxToRem(20)
},
panel: {
backgroundColor: fade('#fff', 0.9)
},
heading: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
fontSize: theme.typography.pxToRem(20),
},
details: {
maxWidth:'95%',
marginLeft: 'auto',
marginRight: 'auto',
marginBottom: 20
}
}))

const AEMODiscoveryInfo = (props) => {

const classes = useStyles()
const [expanded, setExpanded] = React.useState(true)
const {statusDetails, outagesDetails} = props.data

const toggleExpansion = (event, newExpanded) => {
setExpanded(newExpanded)
}

const refreshStatusOutages = () => {
props.retrieveStatus()
props.retrieveOutages()
}

React.useEffect(() => {
refreshStatusOutages()
// eslint-disable-next-line
}, [])

return (
<Accordion defaultExpanded className={classes.panel} expanded={expanded} onChange={toggleExpansion}>
<AccordionSummary
expandIcon={<ExpandMoreIcon/>}
aria-controls='panel1c-content'
>
<div className={classes.heading}>
<SubjectIcon/><Typography style={{paddingLeft: 8}}>Status &amp; Outages</Typography>
</div>
</AccordionSummary>
<div className={classes.details}>
<p>Secondary Data Holders provide data to Data Holders via CDR requests, who in turn provide the data to ADRs. Such data is called Shared Responsibility Data (SR data).</p>
<p>Currently, only the energy sector has a designated secondary data holder: AEMO. This page lists the status and outages for AEMO.</p>

<div className="title"><span><img src="https://www.aemo.com.au/-/media/project/aemo/global/logos/aemo-logo.svg" alt="AEMO"/></span></div>
<StatusOutages statusDetails={statusDetails} outagesDetails={outagesDetails} />
</div>

<Divider/>

<AccordionActions>
<Tooltip title='Refresh'>
<Fab size='medium' color='primary' onClick={refreshStatusOutages}>
<RefreshIcon/>
</Fab>
</Tooltip>
</AccordionActions>
</Accordion>
)
}

const mapStateToProps = state => ({
data: state.aemoDiscovery
})

const mapDispatchToProps = {
retrieveStatus,
retrieveOutages
}

export default connect(mapStateToProps, mapDispatchToProps)(AEMODiscoveryInfo)
24 changes: 18 additions & 6 deletions src/components/data/discovery/DiscoveryInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ const DiscoveryInfo = (props) => {
setExpanded(newExpanded)
}

React.useEffect(() => {
refreshStatusOutages()
// eslint-disable-next-line
}, [props.dataSources])

const getWidth = (dataSourceCount, min) => {
return Math.max(12 / dataSourceCount, min)
}
Expand Down Expand Up @@ -78,8 +83,13 @@ const DiscoveryInfo = (props) => {
{
savedDataSourcesCount > 0 &&
<Grid container alignItems='flex-start' spacing={2} className={classes.container}>
{dataSources.map((dataSource, index) => (
!dataSource.unsaved && dataSource.enabled && !dataSource.deleted &&
{dataSources.map((dataSource, index) => {
const data = props.data[index]
if (!data) {
return false
}
const {statusDetails, outagesDetails} = data
return (!dataSource.unsaved && dataSource.enabled && !dataSource.deleted &&
<Grid item key={index}
xs={getWidth(savedDataSourcesCount, 12)}
sm={getWidth(savedDataSourcesCount, 12)}
Expand All @@ -88,9 +98,10 @@ const DiscoveryInfo = (props) => {
xl={getWidth(savedDataSourcesCount, 3)}
>
<div className="title">{!!dataSource.icon && <span><img src={dataSource.icon} alt=""/></span>}<h2>{dataSource.name}</h2></div>
<StatusOutages dataSource={dataSource} dataSourceIndex={index}/>
<StatusOutages statusDetails={statusDetails} outagesDetails={outagesDetails} />
</Grid>
))}
)
})}
</Grid>
}
</div>
Expand All @@ -108,10 +119,11 @@ const DiscoveryInfo = (props) => {
)
}

const mapStateToProps = state=>({
const mapStateToProps = state => ({
dataSources : state.dataSources,
savedDataSourcesCount: state.dataSources.filter(dataSource => !dataSource.unsaved && !dataSource.deleted && dataSource.enabled).length,
versionInfo: state.versionInfo.vHeaders
versionInfo: state.versionInfo.vHeaders,
data: state.discovery
})

const mapDispatchToProps = {
Expand Down
19 changes: 1 addition & 18 deletions src/components/data/discovery/StatusOutages.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,12 @@ import React from 'react'
import DateTime from '../DateTime'
import Duration from '../Duration'
import { connect } from 'react-redux'
import { normalise } from '../../../utils/url'
import { retrieveStatus, retrieveOutages } from '../../../store/discovery'
import { translateDiscoveryStatus } from '../../../utils/dict'

class StatusOutages extends React.Component {

componentDidMount() {
const { dataSourceIndex, dataSource, versionInfo } = this.props
const url = normalise(dataSource.url)
this.props.retrieveStatus(dataSourceIndex, url, versionInfo.xV, versionInfo.xMinV)
this.props.retrieveOutages(dataSourceIndex, url, versionInfo.xV, versionInfo.xMinV)
}

render() {
let data = this.props.data[this.props.dataSourceIndex]
if (!data) {
return false
}
const {statusDetails, outagesDetails} = data
const { statusDetails, outagesDetails } = this.props
return (
<>
{!!statusDetails &&
Expand Down Expand Up @@ -63,13 +50,9 @@ const Outage = props => {
}

const mapStateToProps = state => ({
versionInfo: state.versionInfo.vHeaders,
data: state.discovery
})

const mapDispatchToProps = {
retrieveStatus,
retrieveOutages
}

export default connect(mapStateToProps, mapDispatchToProps)(StatusOutages)
60 changes: 60 additions & 0 deletions src/store/aemo_discovery/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {conoutInfo} from '../conout/actions'
import {createConoutError, checkExposedHeaders} from '../../utils/cors'

const AEMO_URL = 'https://api.aemo.com.au/NEMRetail/cds-au/v1'

export const RETRIEVE_AEMO_STATUS = 'RETRIEVE_AEMO_STATUS'
export const RETRIEVE_AEMO_OUTAGES = 'RETRIEVE_AEMO_OUTAGES'

const headers = {
'Accept': 'application/json',
'x-v': 1
}

export const retrieveStatus = () => dispatch => {
const fullUrl = AEMO_URL + '/discovery/status'
const request = new Request(fullUrl, {headers})
dispatch(conoutInfo('Requesting retrieveStatus(): ' + fullUrl))
dispatch({
type: RETRIEVE_AEMO_STATUS,
payload: fetch(request)
.then(response => {
if (response.ok) {
checkExposedHeaders(response, fullUrl, dispatch)
return response.json()
}
throw new Error(`Response not OK. Status: ${response.status} (${response.statusText})`)
})
.then(obj => {
dispatch(conoutInfo(`Received response for ${fullUrl}:`, obj))
return obj
})
.catch(error => {
dispatch(createConoutError(error, fullUrl))
})
})
}

export const retrieveOutages = () => dispatch => {
const fullUrl = AEMO_URL + '/discovery/outages'
const request = new Request(fullUrl, {headers})
dispatch(conoutInfo('Requesting retrieveOutages(): ' + fullUrl))
dispatch({
type: RETRIEVE_AEMO_OUTAGES,
payload: fetch(request)
.then(response => {
if (response.ok) {
checkExposedHeaders(response, fullUrl, dispatch)
return response.json()
}
throw new Error(`Response not OK. Status: ${response.status} (${response.statusText})`)
})
.then(obj => {
dispatch(conoutInfo(`Received response for ${fullUrl}:`, obj))
return obj
})
.catch(error => {
dispatch(createConoutError(error, fullUrl))
})
})
}
2 changes: 2 additions & 0 deletions src/store/aemo_discovery/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './actions'
export * from './reducer'
28 changes: 28 additions & 0 deletions src/store/aemo_discovery/reducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {
RETRIEVE_AEMO_STATUS,
RETRIEVE_AEMO_OUTAGES
} from './actions'
import {fulfilled} from '../../utils/async-actions'

export default function discovery(state = {}, action) {
switch (action.type) {
case fulfilled(RETRIEVE_AEMO_STATUS): {
const s = {...state}
if (action.payload) {
const response = action.payload
s.statusDetails = response ? response.data : null
}
return s
}
case fulfilled(RETRIEVE_AEMO_OUTAGES): {
const s = {...state}
if (action.payload) {
const response = action.payload
s.outagesDetails = response ? response.data : null
}
return s
}
default:
return state
}
}
17 changes: 3 additions & 14 deletions src/store/banking/data/actions.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {conoutInfo, conoutError, conoutHtmlError, conoutWarn} from '../../conout/actions'
import {conoutInfo, conoutError, conoutWarn} from '../../conout/actions'
import {createConoutError, checkExposedHeaders} from '../../../utils/cors'
import {encodeRFC3986URIComponent} from '../../../utils/url'

export const START_RETRIEVE_PRODUCT_LIST = 'START_RETRIEVE_PRODUCT_LIST'
Expand All @@ -17,11 +18,6 @@ const headers = {
'Accept': 'application/json'
}

function createConoutError(error, url) {
return conoutError('Caught ' + error + ' while requesting ' + url + (error.name === 'TypeError' ?
' Possibly caused by the endpoint not supporting Cross-Origin Requests (CORS)' : ''))
}

export const retrieveProductList = (dataSourceIdx, baseUrl, productListUrl, xV, xMinV) =>
(dispatch) => {
const request = new Request(productListUrl, {headers: new Headers({...headers, 'x-v': xV, 'x-min-v': xMinV})})
Expand All @@ -31,14 +27,7 @@ export const retrieveProductList = (dataSourceIdx, baseUrl, productListUrl, xV,
payload: fetch(request)
.then(response => {
if (response.ok) {
if (!response.headers['x-v']) {
const msg = `Response for ${productListUrl}: doesn't expose header x-v: possibly caused by incomplete `
const corsSupport = 'CORS support'
dispatch(conoutHtmlError(
msg + corsSupport,
`${msg}<a href="https://cdr-support.zendesk.com/hc/en-us/articles/900003054706-CORS-support" target="_blank">${corsSupport}</a>`
))
}
checkExposedHeaders(response, productListUrl, dispatch)
return response.json()
}
throw new Error(`Response not OK. Status: ${response.status} (${response.statusText})`)
Expand Down
18 changes: 4 additions & 14 deletions src/store/discovery/actions.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {conoutInfo, conoutHtmlError, conoutError} from '../conout/actions'
import {conoutInfo} from '../conout/actions'
import {createConoutError, checkExposedHeaders} from '../../utils/cors'

export const RETRIEVE_STATUS = 'RETRIEVE_STATUS'
export const RETRIEVE_OUTAGES = 'RETRIEVE_OUTAGES'
Expand All @@ -7,11 +8,6 @@ const headers = {
'Accept': 'application/json'
}

function createConoutError(error, url) {
return conoutError('Caught ' + error + ' while requesting ' + url + (error.name === 'TypeError' ?
' Possibly caused by the endpoint not supporting Cross-Origin Requests (CORS)' : ''))
}

export const retrieveStatus = (dataSourceIdx, url, xV, xMinV) => dispatch => {
const fullUrl = url + '/discovery/status'
const request = new Request(fullUrl, {
Expand All @@ -23,6 +19,7 @@ export const retrieveStatus = (dataSourceIdx, url, xV, xMinV) => dispatch => {
payload: fetch(request)
.then(response => {
if (response.ok) {
checkExposedHeaders(response, fullUrl, dispatch)
return response.json()
}
throw new Error(`Response not OK. Status: ${response.status} (${response.statusText})`)
Expand All @@ -48,14 +45,7 @@ export const retrieveOutages = (dataSourceIdx, url, xV, xMinV) => dispatch => {
payload: fetch(request)
.then(response => {
if (response.ok) {
if (!response.headers['x-v']) {
const msg = `Response for ${fullUrl}: doesn't expose header x-v: possibly caused by incomplete `
const corsSupport = 'CORS support'
dispatch(conoutHtmlError(
msg + corsSupport,
`${msg}<a href="https://cdr-support.zendesk.com/hc/en-us/articles/900003054706-CORS-support" target="_blank">${corsSupport}</a>`
))
}
checkExposedHeaders(response, fullUrl, dispatch)
return response.json()
}
throw new Error(`Response not OK. Status: ${response.status} (${response.statusText})`)
Expand Down
Loading