Skip to content

Commit

Permalink
Merge pull request #39 from HuygensING/feature/mutation-facetconfig
Browse files Browse the repository at this point in the history
Feature/mutation facetconfig
  • Loading branch information
jariz authored Dec 7, 2017
2 parents d9dd3ef + d3519ad commit 5341394
Show file tree
Hide file tree
Showing 15 changed files with 107 additions and 53 deletions.
2 changes: 2 additions & 0 deletions src/components/ProgressBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const ProgressWrapper = styled.div`

const ProgressLabel = styled(Label)`
display: inline-block;
text-overflow: ellipsis;
direction: rtl;
overflow: hidden;
width: 10rem;
margin-right: 1rem;
Expand Down
2 changes: 2 additions & 0 deletions src/components/form/fields/ConnectedSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { compose } from 'redux';
import { default as metaDataResolver, MetaDataProps } from '../../../services/metaDataResolver';
import { RDF_TYPE } from '../../../constants/global';
import verifyResponse from '../../../services/verifyResponse';
import { branch, renderNothing } from 'recompose';

interface OwnProps extends SelectProps, CollectionEditViewProps {
onChange: (value: string, property: Property) => void;
Expand Down Expand Up @@ -52,5 +53,6 @@ const SelectField: SFC<Props> = ({ name, selected, metadata, onChange, shownAsMu
export default compose<SFC<OwnProps>>(
withRouter,
metaDataResolver<Props>(QUERY_COLLECTION_EDIT_VIEW),
branch((props: Props) => props.metadata.loading, renderNothing),
verifyResponse<Props, 'metadata'>('metadata', 'dataSetMetadata')
)(SelectField);
2 changes: 1 addition & 1 deletion src/components/routes/Entry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const dataResolver = compose<SFC<{}>>(
verifyResponse<FullProps, 'metadata'>('metadata', 'dataSetMetadata'),
graphqlWithProps<FullProps>(QUERY_ENTRY_VALUES),
renderLoader(),
verifyResponse<FullProps, 'data'>('data', 'dataSetMetadata.collection')
verifyResponse<FullProps, 'data'>('data', 'dataSets')
);

export default dataResolver(Entry);
48 changes: 42 additions & 6 deletions src/components/routes/FacetConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import graphToState from '../../services/graphToState';
import { RootState } from '../../reducers/rootReducer';
import { NormalizedFacetConfig } from '../../typings';
import verifyResponse from '../../services/verifyResponse';
import { ChildProps } from 'react-apollo';
import graphql from 'react-apollo/graphql';
import { FacetConfig } from '../../typings/schema';
import gql from 'graphql-tag';

interface StateProps {
normalizedFacets: NormalizedFacetConfig[];
Expand All @@ -26,17 +30,40 @@ type FullProps = MetaDataProps &
RouteComponentProps<{ dataSet: string; collection: string }> &
FormWrapperProps;

interface GraphIndexConfig {
facet: FacetConfig[];
fullText?: {
caption: '';
fields: { path: string }[];
};
}

type GraphProps = ChildProps<FullProps, { dataSet: string; collectionUrl: string; indexConfig: GraphIndexConfig }>;

const Section = styled.div`
width: 100%;
padding-bottom: 3rem;
`;

const FacetConfig: SFC<FullProps> = props => {
const onSubmit = () => {
const facetconfig = denormalizeFacets(props.normalizedFacets);
console.groupCollapsed('sending facet config:');
console.log(facetconfig);
console.groupEnd();
const FacetConfig: SFC<GraphProps> = props => {
const onSubmit = async () => {
const { dataSetId, collection } = props.metadata.dataSetMetadata!;

try {
const facet = denormalizeFacets(props.normalizedFacets);
const indexConfig: GraphIndexConfig = {
facet,
fullText: {
caption: '',
fields: []
} // TODO: Implement a full text configurable option for the facets
};

await props.mutate!({ variables: { dataSet: dataSetId, collectionUri: collection!.uri, indexConfig } });
alert(`The facet configuration for collection ${collection!.collectionId} has been updated`);
} catch (e) {
alert(e); // TODO: Make this fancy, I'd suggest to maybe add an optional error to NormalizedFacetConfig, add a scrollTo and style the selectBox accordingly
}
};

return (
Expand All @@ -50,6 +77,14 @@ const FacetConfig: SFC<FullProps> = props => {
);
};

const submitFacetConfig = gql`
mutation submitFacetConfig($dataSet: ID!, $collectionUri: String!, $indexConfig: IndexConfigInput!) {
setIndexConfig(dataSet: $dataSet, collectionUri: $collectionUri, indexConfig: $indexConfig) {
__typename
}
}
`;

const mapStateToProps = (state: RootState) => ({
normalizedFacets: state.facetconfig
});
Expand All @@ -59,6 +94,7 @@ export default compose<SFC<{}>>(
metaDataResolver<FullProps>(QUERY_COLLECTION_PROPERTIES),
renderLoader('metadata'),
verifyResponse<FullProps, 'metadata'>('metadata', 'dataSetMetadata.collection'),
graphql(submitFacetConfig),
connect(mapStateToProps),
graphToState<FullProps>('GRAPH_TO_FACETCONFIG', 'metadata', false)
)(FacetConfig);
10 changes: 3 additions & 7 deletions src/components/routes/Search.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { ComponentType, SFC } from 'react';
import { connect, Dispatch } from 'react-redux';
import { compose } from 'redux';
import verifyResponse from '../../services/verifyResponse';
import verifyResponse, { handleError } from '../../services/verifyResponse';
import { ChildProps } from '../../typings';
import { lifecycle, withProps } from 'recompose';
import { RouteComponentProps, withRouter } from 'react-router';
Expand Down Expand Up @@ -133,15 +133,11 @@ const mapDispatchToProps = (dispatch: Dispatch<FullProps>) => ({

const dataResolver = compose<ComponentType<{}>>(
withRouter,
metaDataResolver<ApolloProps>(QUERY_COLLECTION_PROPERTIES),
metaDataResolver<ApolloProps>(QUERY_COLLECTION_PROPERTIES), // TODO: Need to think about a refetch option, it now caches configuration for facets
renderLoader('metadata'),
verifyResponse<FullProps, 'metadata'>('metadata', 'dataSetMetadata.collection'),
graphqlWithProps<ApolloProps>(QUERY_COLLECTION_VALUES),
verifyResponse<FullProps, 'data'>(
'data',
props =>
`dataSets.${props.match.params.dataSet}.${props.metadata.dataSetMetadata!.collection!.collectionListId}`
),
handleError('data'),
connect(mapStateToProps, mapDispatchToProps),
withProps(({ data, metadata }: FullProps): ExtraProps => ({
collectionValues: getCollectionValues(data, metadata)
Expand Down
15 changes: 7 additions & 8 deletions src/components/routes/ViewConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,16 @@ const Section = styled.div`
`;

const ViewConfig: SFC<GraphProps> = props => {
const onSubmit = () => {
const onSubmit = async () => {
const { dataSetId, collection } = props.metadata.dataSetMetadata!;
const viewConfig = props.denormalizeTree();

if (typeof viewConfig === 'string') {
return alert(viewConfig); // TODO: Make this fancy, I'd suggest to maybe add an optional error to NormalizedComponentConfig, add a scrollTo and style the selectBox accordingly
try {
const viewConfig = props.denormalizeTree();
await props.mutate!({ variables: { dataSet: dataSetId, collectionUri: collection!.uri, viewConfig } });
alert(`The collection ${collection!.collectionId} has been updated`);
} catch (e) {
alert(e); // TODO: Make this fancy, I'd suggest to maybe add an optional error to NormalizedComponentConfig, add a scrollTo and style the selectBox accordingly
}

return props.mutate!({ variables: { dataSet: dataSetId, collectionUri: collection!.uri, viewConfig } })
.then(data => alert(`The collection ${collection!.collectionId} has been updated`)) // TODO: This also should be something fancy
.catch((err: Error) => console.error('there was an error sending the query', err));
};

return (
Expand Down
23 changes: 16 additions & 7 deletions src/reducers/facetconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { CollectionMetadata, FacetConfig } from '../typings/schema';
import { NormalizedFacetConfig, ReferencePath } from '../typings/index';
import { arrayMove } from 'react-sortable-hoc';
import { createReferencePath, mendPath } from '../services/walkPath';
import { facetErrors } from '../services/Validation';
import { MetaDataProps } from '../services/metaDataResolver';

// state def
Expand Down Expand Up @@ -63,19 +64,27 @@ export const getById = (id: number, state: FacetConfigReducer): NormalizedFacetC
state.find(config => config.id === id);

export const denormalizeFacetConfig = (config: NormalizedFacetConfig): FacetConfig => {
config = { ...config };
const denormalizedConfig = { ...config, paths: [] as string[] };

for (const [idx, referencePath] of config.referencePaths.entries()) {
config.paths[idx] = mendPath(referencePath);
for (const referencePath of config.referencePaths) {
const error = facetErrors(referencePath, config);

if (error) {
throw error;
}
denormalizedConfig.paths.push(mendPath(referencePath));
}

delete config.referencePaths;
delete config.id;
return config;
delete denormalizedConfig.referencePaths;
delete denormalizedConfig.id;
if (denormalizedConfig.__typename) {
delete denormalizedConfig.__typename;
}
return denormalizedConfig;
};

export const denormalizeFacets = (facetConfigs: NormalizedFacetConfig[]): FacetConfig[] =>
facetConfigs.map(denormalizeFacetConfig); // TODO: make sure it returns a message or something in case of error
facetConfigs.map(denormalizeFacetConfig);

// reducer
const references = (payload: { facetConfig: { paths: string[] }; collectionId: string }): ReferencePath[] => {
Expand Down
8 changes: 5 additions & 3 deletions src/reducers/search.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Facet, FacetConfig, FacetOption } from '../typings/schema';
import { Location } from 'history';
import * as queryString from 'querystring';
import { EsValuePath } from '../services/EsQueryStringCreator';

export interface FullTextSearch {
dataset: Readonly<string>;
Expand Down Expand Up @@ -75,7 +76,9 @@ const mergeFacets = (configs: FacetConfig[], facets: Facet[]): EsFilter[] =>
const setNewSelected = (newFilters: EsFilter[], key: string, valueName: string): void => {
newFilters.forEach((newFilter, filterIdx) => {
newFilter.paths.forEach(path => {
if (path === key) {
const trimmedPath = EsValuePath(path);

if (trimmedPath === key) {
newFilter.values.forEach((newValue, valueIdx) => {
if (newValue.name === valueName) {
const value = { ...newValue, selected: true };
Expand Down Expand Up @@ -104,8 +107,7 @@ export const mergeOldSelected = (newFilters: EsFilter[], location: Location): vo

matches.bool.should.forEach(obj => {
const key = Object.keys(obj.match)[0];
const value = obj.match[key];
return setNewSelected(newFilters, key.slice(0, -4), value);
return setNewSelected(newFilters, key, obj.match[key]);
});
}
);
Expand Down
14 changes: 5 additions & 9 deletions src/reducers/viewconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ const denormalizePath = (childNode: NormalizedComponentConfig): NormalizedCompon
return childNode;
};

const denormalizeNode = (item: NormalizedComponentConfig, state: ViewConfigReducer): ComponentConfig | string => {
const denormalizeNode = (item: NormalizedComponentConfig, state: ViewConfigReducer): ComponentConfig => {
item = denormalizePath(item);
item.subComponents = [];

Expand All @@ -146,12 +146,12 @@ const denormalizeNode = (item: NormalizedComponentConfig, state: ViewConfigReduc
if (childNode) {
const error = componentErrors(childNode);
if (error) {
return error;
throw error;
}

const denormalizedChildNode = denormalizeNode(childNode, state);

if (denormalizedChildNode && typeof denormalizedChildNode !== 'string') {
if (denormalizedChildNode) {
item.subComponents.push(denormalizedChildNode);
}
}
Expand All @@ -168,7 +168,7 @@ const denormalizeNode = (item: NormalizedComponentConfig, state: ViewConfigReduc
return item;
};

export const denormalizeTree = (state: ViewConfigReducer): ComponentConfig[] | string => {
export const denormalizeTree = (state: ViewConfigReducer): ComponentConfig[] => {
let denormalizedTree: ComponentConfig[] = [];

const parentNode = getNodeById(0, state)!;
Expand All @@ -178,16 +178,12 @@ export const denormalizeTree = (state: ViewConfigReducer): ComponentConfig[] | s
if (rootNode) {
const error = componentErrors(rootNode);
if (error) {
return error;
throw error;
}

const denormalizedRootNode = denormalizeNode(rootNode, state);

if (denormalizedRootNode) {
if (typeof denormalizedRootNode === 'string') {
return denormalizedRootNode;
}

denormalizedTree.push(denormalizedRootNode);
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/services/CollectionArgumentsCreator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import queryString from 'querystring';
import { FacetConfig, IndexConfig } from '../typings/schema';
import { EsMatches, EsQuery, setFirstPathAsString } from './EsQueryStringCreator';
import { EsMatches, EsQuery, EsValuePath } from './EsQueryStringCreator';
import { Location } from 'history';

interface Aggs {
Expand Down Expand Up @@ -70,7 +70,7 @@ const createAggsString = (facets: FacetConfig[], searchObj: EsQuery | null): Agg
const entries = facets.entries();
for (const [idx, { paths, caption, type }] of entries) {
if (type === 'MultiSelect' && (caption || type) && paths) {
const field = setFirstPathAsString(paths);
const field = EsValuePath(paths[0]);
const filter = searchObj ? setFilteredSearchObj(searchObj, field) : {};

aggregations[caption || `${type}_${idx}`] = {
Expand Down
11 changes: 6 additions & 5 deletions src/services/EsQueryStringCreator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { EsFilter, FullTextSearch } from '../reducers/search';
import { PATH_SPLIT, splitPath } from './walkPath';

export interface EsQuery {
bool: EsBool;
Expand Down Expand Up @@ -28,12 +29,12 @@ export interface EsMatch {
};
}

/** create a string from the first path and append it with the for ES needed value
/** Create path that only holds values for elasticsearch querying
*
* @param {string[]} paths
* @returns {string}
* @param {string} path
* @constructor
*/
export const setFirstPathAsString = (paths: string[]): string => `${paths[0]}.raw`;
export const EsValuePath = (path: string) => `${splitPath(path, true).join(PATH_SPLIT)}.raw`;

/** retrieve all selected values, create a new 'match' for each of them and add them to the query
*
Expand All @@ -48,7 +49,7 @@ const addMatchQueries = (filters: Readonly<EsFilter[]>, query: EsQuery): void =>
filter.values.forEach(value => {
if (value.selected) {
hasValues = true;
const newMatch: EsMatch = { match: { [setFirstPathAsString(filter.paths)]: value.name } };
const newMatch: EsMatch = { match: { [EsValuePath(filter.paths[0])]: value.name } };
matches.bool.should.push(newMatch);
}
});
Expand Down
16 changes: 13 additions & 3 deletions src/services/Validation.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { NormalizedComponentConfig } from '../typings/index';
import { COMPONENTS } from '../constants/global';
import { NormalizedComponentConfig, NormalizedFacetConfig, ReferencePath } from '../typings/index';
import { COMPONENTS, VALUE } from '../constants/global';

const referenceIncomplete = (path: string[][] | undefined) => path && path[path.length - 1][1] !== VALUE;

export const componentErrors = (childNode: NormalizedComponentConfig): string | null => {
// has a selectpath, but is not completed
if (childNode.referencePath && childNode.referencePath[childNode.referencePath.length - 1][0] !== 'VALUE') {
if (referenceIncomplete(childNode.referencePath)) {
return `You forgot to finish the path for ${childNode.name}: ${childNode.value}`;
}

Expand All @@ -17,3 +19,11 @@ export const componentErrors = (childNode: NormalizedComponentConfig): string |

return null;
};

export const facetErrors = (referencePath: ReferencePath, config: NormalizedFacetConfig): string | null => {
if (referenceIncomplete(referencePath)) {
return `You forgot to finish the path ${config.caption}: ${referencePath}`;
}

return null;
};
2 changes: 1 addition & 1 deletion src/services/verifyResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ interface SinkProps {

type DataProps<TProps, K extends keyof TProps> = { [P in K]: QueryProps };

const handleError = <TProps extends DataProps<TProps, K>, K = keyof TProps>(dataProp: K) =>
export const handleError = <TProps extends DataProps<TProps, K>, K = keyof TProps>(dataProp: K) =>
branch<TProps>((props: any) => !!props[dataProp].error, renderNothing);

const ensureExistence = <TProps>(path: string | ((props: TProps) => string), dataProp: keyof TProps) => {
Expand Down
2 changes: 1 addition & 1 deletion src/services/walkPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const walkPath = (pathStr: string | undefined, formatters: FormatterConfi
};

export const createReferencePath = (path: string, collectionId: string): ReferencePath =>
path.length > 0 ? [[collectionId], ...(splitPath(path) as ReferencePath)] : [[collectionId]];
path.length > 0 ? (splitPath(path) as ReferencePath) : [[collectionId]];

function isValue(obj: Value | Entity): obj is Value {
return obj.hasOwnProperty(VALUE);
Expand Down
1 change: 1 addition & 0 deletions src/typings/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ export interface FacetConfig {
paths: string[];
type: FacetConfigType;
caption: string | null;
__typename?: string;
}

export type FacetConfigType = 'MultiSelect' | 'DateRange' | 'Hierarchical';
Expand Down

0 comments on commit 5341394

Please sign in to comment.