Skip to content

Commit

Permalink
Merge pull request #37 from kids-first/show-referenced
Browse files Browse the repository at this point in the history
✨References include resources referenced by the given ID
  • Loading branch information
abgeorge7 authored Mar 13, 2020
2 parents d97ee30 + ac8f7cc commit 8a364e7
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 67 deletions.
114 changes: 75 additions & 39 deletions src/components/tables/ReferenceTable.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Modal} from 'semantic-ui-react';
import {getReferencedBy} from '../../utils/api';
import {getReferencedBy, getReferences} from '../../utils/api';
import SortableTable from './SortableTable';
import './ReferenceTable.css';

Expand All @@ -10,59 +10,81 @@ class ReferenceTable extends React.Component {
super(props);
this.state = {
loadingReferences: false,
referenceData: [],
referencingData: [],
referencedByData: [],
showChildModal: null,
};
}

componentDidMount() {
this.fetchReferences();
this.fetchAllReferences();
}

componentDidUpdate(prevProps) {
if (this.props.resourceId !== prevProps.resourceId) {
this.fetchReferences();
if (this.props.resource.resourceId !== prevProps.resource.resourceId) {
this.fetchAllReferences();
}
}

fetchReferences = async () => {
fetchAllReferences = async () => {
this.setState({loadingReferences: true}, async () => {
this.props.setLoadingMessage(
`Fetching references for ${this.props.resourceId}...`,
`Fetching references for ${this.props.resource.id}...`,
);
const references = await getReferencedBy(
this.props.baseUrl,
this.props.resourceType,
this.props.resourceId,
);
let uniqueReferences = {};
references.forEach(reference => {
const mapValue = uniqueReferences[reference.profile];
if (!!mapValue) {
uniqueReferences[reference.profile] = {
...mapValue,
total: mapValue.total + 1,
children: mapValue.children.concat(reference),
};
} else {
uniqueReferences[reference.profile] = {
id: `${reference.resourceType}-${reference.name}`,
resourceType: reference.resourceType,
name: reference.name,
profile: reference.profile,
total: 1,
children: [reference],
};
}
});
const referenceData = Object.values(uniqueReferences);
const referencedByData = await this.fetchReferencedBy();
const referencingData = await this.fetchReferencing();
this.setState({
referenceData,
referencedByData,
referencingData,
loadingReferences: false,
});
});
};

fetchReferencedBy = async () => {
const references = await getReferencedBy(
this.props.baseUrl,
this.props.resource.resourceType,
this.props.resource.id,
);
return this.getReferenceMap(references);
};

fetchReferencing = async () => {
const referencingIds = Object.keys(this.props.resource)
.map(field => this.props.resource[field].reference)
.filter(field => field);
const allReferences = await getReferences(
this.props.baseUrl,
referencingIds,
);
return this.getReferenceMap(allReferences);
};

getReferenceMap = references => {
let uniqueReferences = {};
references.forEach(reference => {
const mapValue = uniqueReferences[reference.profile];
if (!!mapValue) {
uniqueReferences[reference.profile] = {
...mapValue,
total: mapValue.total + 1,
children: mapValue.children.concat(reference),
};
} else {
uniqueReferences[reference.profile] = {
id: `${reference.resourceType}-${reference.name}`,
resourceType: reference.resourceType,
name: reference.name,
profile: reference.profile,
total: 1,
children: [reference],
};
}
});
return Object.values(uniqueReferences);
};

onChildRowClick = child => {
this.setState({showChildModal: child});
};
Expand All @@ -81,12 +103,20 @@ class ReferenceTable extends React.Component {
>
<p>{this.props.loadingMessage}</p>
</div>
{this.state.referenceData && !this.state.loadingReferences ? (
{(this.state.referencedByData || this.state.referencingData) &&
!this.state.loadingReferences ? (
<div>
<h3>Resources that reference {this.props.resourceId}:</h3>
<h3>Resources that reference {this.props.resource.id}:</h3>
<SortableTable
headerCells={this.props.tableHeaders}
data={this.state.referencedByData}
rowChildren={true}
onChildRowClick={this.onChildRowClick}
/>
<h3>Resources referenced by {this.props.resource.id}:</h3>
<SortableTable
headerCells={this.props.tableHeaders}
data={this.state.referenceData}
data={this.state.referencingData}
rowChildren={true}
onChildRowClick={this.onChildRowClick}
/>
Expand Down Expand Up @@ -116,8 +146,10 @@ class ReferenceTable extends React.Component {
}

ReferenceTable.propTypes = {
resourceId: PropTypes.string.isRequired,
resourceType: PropTypes.string.isRequired,
resource: PropTypes.shape({
id: PropTypes.string.isRequired,
resourceType: PropTypes.string.isRequired,
}),
tableHeaders: PropTypes.arrayOf(
PropTypes.shape({
display: PropTypes.string.isRequired,
Expand All @@ -132,6 +164,10 @@ ReferenceTable.propTypes = {
};

ReferenceTable.defaultProps = {
resource: {
id: '',
resourceType: '',
},
onClick: () => {},
loadingMessage: '',
};
Expand Down
5 changes: 1 addition & 4 deletions src/components/tables/ResultsTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,10 +252,7 @@ class ResultsTable extends React.Component {
<ReferenceTable
onClick={this.onReferenceRowClick}
tableHeaders={referencedByTableHeaders}
resourceId={this.state.rowData ? this.state.rowData.id : ''}
resourceType={
this.state.rowData ? this.state.rowData.resourceType : ''
}
resource={this.state.rowData ? this.state.rowData : null}
baseUrl={this.props.baseUrl}
loadingMessage={this.props.loadingMessage}
setLoadingMessage={this.props.setLoadingMessage}
Expand Down
66 changes: 42 additions & 24 deletions src/utils/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,31 +91,49 @@ export const getCapabilityStatementReferences = async (url, resourceType) =>
});

export const getReferencedBy = async (url, baseType, id) => {
let allReferences = await getCapabilityStatementReferences(
`${url}metadata`,
baseType,
);
let resourceReferences = await Promise.all(
allReferences.map(async ref => {
const data = await fetchAllResources(
`${url}${ref.type}?${baseType.toLowerCase()}=${baseType}/${id}`,
[],
);
return data.flat();
if (baseType) {
let allReferences = await getCapabilityStatementReferences(
`${url}metadata`,
baseType,
);
let resourceReferences = await Promise.all(
allReferences.map(async ref => {
const data = await fetchAllResources(
`${url}${ref.type}?${baseType.toLowerCase()}=${baseType}/${id}`,
[],
);
return data.flat();
}),
);
resourceReferences = [].concat
.apply([], resourceReferences)
.map(item => item.resource);
return await formatReferences(url, resourceReferences);
} else {
return [];
}
};

export const getReferences = async (url, referenceIds) => {
const resources = await Promise.all(
referenceIds.map(async reference => {
const resource = await fetchResource(`${url}${reference}`);
return resource;
}),
);
resourceReferences = [].concat
.apply([], resourceReferences)
.map(item => item.resource)
.map(item => ({
...item,
profile:
item.meta && item.meta.profile
? item.meta.profile
: [`${fhirUrl}${item.resourceType}`],
}));
resourceReferences = await Promise.all(
resourceReferences.map(async ref => {
return await formatReferences(url, resources);
};

export const formatReferences = async (url, references) => {
references = references.map(item => ({
...item,
profile:
item.meta && item.meta.profile
? item.meta.profile
: [`${fhirUrl}${item.resourceType}`],
}));
references = await Promise.all(
references.map(async ref => {
const data = await fetchResource(
`${url}StructureDefinition?url=${ref.profile[0]}`,
);
Expand All @@ -126,7 +144,7 @@ export const getReferencedBy = async (url, baseType, id) => {
return {...ref, name};
}),
);
return resourceReferences.filter(ref => ref.name);
return references.filter(ref => ref.name);
};

export const userIsAuthorized = (username, password, baseUrl) => {
Expand Down

0 comments on commit 8a364e7

Please sign in to comment.