Skip to content

Commit

Permalink
Updated schema expressions (implement tree; matcher doesn't handle th…
Browse files Browse the repository at this point in the history
…is yet.)

Fixed text refresh bug: #22
Fixed user tasks bug.
  • Loading branch information
NAME authored and NAME committed Dec 7, 2016
1 parent 1751499 commit 3ea9ee1
Show file tree
Hide file tree
Showing 14 changed files with 103 additions and 89 deletions.
6 changes: 6 additions & 0 deletions sub/apollo/src/client/view/component/status.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,17 @@ export class StatusBar extends React.Component {
render() {
function state(state) { return state ? ' app-icon-on' : '' }

// TODO(burdon): Move to config.
const github = 'https://github.com/alienlaboratories/react-demos/issues';

return (
<div className="app-toolbar app-status-toolbar">
<div>
<i className="app-icon app-icon-action material-icons" title="Debug info"
onClick={ this.handleAction.bind(this, 'bug') }>bug_report</i>
<a href={ github } target="MINDER_GITHUB">
<i className="app-icon app-icon-action material-icons" title="Report Bug">report_problem</i>
</a>
<a href="/graphiql" target="MINDER_GRAPHIQL" title="GraphiQL">
<i className="app-icon app-icon-action material-icons" title="GraphiQL">language</i>
</a>
Expand Down
2 changes: 1 addition & 1 deletion sub/apollo/src/client/view/component/type/group.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const GroupFragments = {
type
title
tasks(filter: { predicate: { field: "assignee", ref: "id" } }) {
tasks(filter: { expr: { field: "assignee", ref: "id" } }) {
id
type
title
Expand Down
6 changes: 4 additions & 2 deletions sub/apollo/src/client/view/component/type/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { ID } from 'minder-core';
*/
export const UserFragments = {

// TODO(burdon): Use ref in tasks filter.

// TODO(burdon): Pass variables to fragments (e.g., filter tasks)?
// https://github.com/apollostack/react-apollo/issues/140
// https://github.com/apollostack/react-apollo/issues/122
Expand All @@ -24,12 +26,12 @@ export const UserFragments = {
fragment UserFragment on User {
title
ownerTasks: tasks(filter: { predicate: { field: "owner" } }) {
ownerTasks: tasks(filter: { expr: { field: "owner", ref: "id" } }) {
id
title
}
assigneeTasks: tasks(filter: { predicate: { field: "assignee" } }) {
assigneeTasks: tasks(filter: { expr: { field: "assignee", ref: "id" } }) {
id
title
}
Expand Down
2 changes: 1 addition & 1 deletion sub/apollo/src/client/view/component/type_registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ registry._types.set('Group', {
return _.compact(_.map(tasks, (task) => {
if (task.id == item.id) {
// TODO(burdon): Extract filter from query and use matcher to determine if remove.
const filter = { predicate: { field: "assignee", value: { id: member.id } } };
const filter = { expr: { field: "assignee", value: { id: member.id } } };
if (matcher.matchItem({}, filter, item)) {
return item;
}
Expand Down
1 change: 0 additions & 1 deletion sub/apollo/src/client/view/detail.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ const DetailQuery = gql`
}
`;


// TODO(burdon): New Synax: http://dev.apollodata.com/react/fragments.html
// ${CommentsPage.fragments.comment} instead of createFragment/query.fragments option below.

Expand Down
10 changes: 3 additions & 7 deletions sub/apollo/src/client/view/folder.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,7 @@ const FolderQuery = gql`
folders {
id
filter {
type
labels
text
}
filter
}
}
`;
Expand Down Expand Up @@ -173,12 +169,12 @@ export default compose(
// TODO(burdon): Solution is set the redux state in the layout? so can be used above in props?
// TODO(burdon): Handler error/redirect if not found.

// Create list filter (if not set by search above).
// Create list filter (if not overridden by text search above).
if (QueryParser.isEmpty(filter)) {
_.each(folders, (folder) => {
// TODO(burdon): Match folder's short name rather than ID.
if (folder.id == ownProps.params.folder) {
filter = _.omit(folder.filter, '__typename');
filter = JSON.parse(folder.filter);
return false;
}
});
Expand Down
6 changes: 1 addition & 5 deletions sub/apollo/src/client/view/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,7 @@ const LayoutQuery = gql`
folders {
id
filter {
type
labels
text
}
filter
}
}
`;
Expand Down
2 changes: 1 addition & 1 deletion sub/apollo/src/server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const appRouter = (authManager, clientManager, options) => {
graphql: '/graphql'
});

console.log('App Options = %s', JSON.stringify(options));
console.log('Client Options = %s', JSON.stringify(options));

// Webpack assets.
router.use('/assets', express.static(options.assets));
Expand Down
16 changes: 13 additions & 3 deletions sub/apollo/src/server/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,20 +89,30 @@ const database = new Database(matcher)

//
// Database.
// TODO(burdon): Note all of this is asynchronous. Needs chaining/injector.
//

let context = {};

// Load test data.
_.each(require('./testing/test.json'), (items, type) => {
database.upsertItems(context, _.map(items, (item) => ({ type, ...item })));
console.log('TYPE: %s', type);

// Iterate items per type.
database.upsertItems(context, _.map(items, (item) => {

// TODO(burdon): Reformat folders.
if (type == 'Folder') {
item.filter = JSON.stringify(item.filter);
}

return { type, ...item };
}));
});

// Create test data.
promises.push(database.queryItems({}, {}, { type: 'User' })
.then(users => {
console.log('USERS', JSON.stringify(users));
console.log('USERS: %s', JSON.stringify(users));

// Create group.
return database.getItem(context, 'Group', 'minderlabs')
Expand Down
15 changes: 8 additions & 7 deletions sub/core/src/data/matcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class Matcher {
}

// Must match something.
if (!(filter.type || filter.labels || filter.text || filter.predicate)) {
if (!(filter.type || filter.labels || filter.text || filter.expr)) {
return false;
}

Expand All @@ -56,21 +56,22 @@ export class Matcher {
return false;
}

// Predicate match.
if (filter.predicate) {
console.assert(filter.predicate.field);
// Expression match.
// TODO(burdon): Handle AST.
if (filter.expr) {
console.assert(filter.expr.field);

// TODO(burdon): Handle null.
let value = filter.predicate.value;
let value = filter.expr.value;

// Substitute value for reference.
let ref = filter.predicate.ref;
let ref = filter.expr.ref;
if (ref) {
value = _.get(root, ref);
}

// TODO(burdon): Other operators.
if (_.get(item, filter.predicate.field) != value) {
if (_.get(item, filter.expr.field) != value) {
return false;
}
}
Expand Down
4 changes: 2 additions & 2 deletions sub/core/src/data/matcher.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ describe('Matcher:', () => {

let root = {};

expect(matcher.matchItems(root, { predicate: { field: 'owner', value: 'b'} }, items)).to.have.length(2);
expect(matcher.matchItems(root, { expr: { field: 'owner', value: 'b'} }, items)).to.have.length(2);
});

/**
Expand All @@ -78,6 +78,6 @@ describe('Matcher:', () => {
id: 'a'
};

expect(matcher.matchItems(root, { predicate: { field: 'assignee', ref: 'id'} }, items)).to.have.length(1);
expect(matcher.matchItems(root, { expr: { field: 'assignee', ref: 'id'} }, items)).to.have.length(1);
});
});
81 changes: 41 additions & 40 deletions sub/graphql/src/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,22 @@ scalar Date
# Input types.
#

# TODO(burdon): Why can't inputs be used for outputs?
# > https://github.com/graphql/graphql-js/issues/599 [me: 11/15/16]
# > https://github.com/graphql/graphql-js/issues/423 [me: 11/15/16]
# > https://github.com/apollostack/graphql-tools/issues/179

# TODO(burdon): Predicate operator.
# TODO(burdon): Build AST for query syntax? Boolean logic.
# TODO(burdon): Generalize other filter fields to predicates (except type).

# http://docs.oracle.com/javadb/10.8.3.0/ref (WHERE boolean expression)
# https://github.com/ghislainfourny/jsoniq-tutorial
# http://www.jinqjs.com/Examples
# https://jinqjs.readme.io/docs/where-filter

# TODO(burdon): Serialize for stored queries (rather than duplicate types).

input ValueInput {
int: Int
float: Float
Expand All @@ -52,7 +68,6 @@ input ArrayMutationInput {
#
# Object mutation.
# E.g., { field: 'title', value: { string: 'Minder' } }
# TODO(burdon): More compact would be { title: { string: 'Minder' }}
#

input ObjectMutationInput {
Expand All @@ -64,16 +79,28 @@ input ObjectMutationInput {
# Query/Filter primitives.
#

# TODO(burdon): Why can't inputs be used for outputs?
# https://github.com/graphql/graphql-js/issues/599
# https://github.com/graphql/graphql-js/issues/423
# https://github.com/apollostack/graphql-tools/issues/179
enum Operator {
AND,
OR,
NOT
}

# TODO(burdon): Predicate operator.
# TODO(burdon): Build AST for query syntax? Boolean logic.
# TODO(burdon): Generalize other filter fields to predicates (except type).
enum Comparator {
EQ,
GT,
LT,
IN
}

# TODO(burdon): Should be union (boolean, comparator) but union inputs are not supported.
# union Expression = BooleanExpression | ComparatorExpression
# { op: 'OR', expr: [{ field: 'owner', ref: 'id' }, { field: 'assignee', ref: 'id' }] }

input PredicateInput {
input ExpressionInput {
op: Operator
expr: [ExpressionInput]

comp: Comparator # TODO(burdon): Default to EQ?
field: String!
value: ValueInput
ref: String
Expand All @@ -88,39 +115,12 @@ input FilterInput {
labels: [String]
text: String

predicate: PredicateInput
}

#
# Output types
#

type Value {
int: Int
float: Float
string: String
boolean: Boolean

id: ID
date: Date
}

type Predicate {
field: String!
value: Value
ref: String
}

type Filter {
strict: Boolean
type: String
labels: [String]
text: String
predicate: Predicate
# Expression tree.
expr: ExpressionInput
}

#
# Misc types.
# Basic types.
#

type Geo {
Expand Down Expand Up @@ -183,7 +183,8 @@ type Folder implements Item {
title: String!
labels: [String]

filter: Filter
# JSON serialized FilterInput.
filter: String!
}

type Contact implements Item {
Expand Down
26 changes: 15 additions & 11 deletions sub/graphql/tools/babel/update_schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,20 @@ if (!fs.existsSync(dist)) {
//

(async () => {

const schema = makeExecutableSchema({
typeDefs: Schema
});

let result = await (graphql(schema, introspectionQuery));
if (result.errors) {
console.error('Schema Error', JSON.stringify(result.errors, null, 2));
} else {
fs.writeFileSync(filename, JSON.stringify(result, null, 2));
console.log('Created: %s', filename);
console.log('Creating schema...');
try {
const schema = makeExecutableSchema({
typeDefs: Schema
});

let result = await (graphql(schema, introspectionQuery));
if (result.errors) {
console.error('Schema Error', JSON.stringify(result.errors, null, 2));
} else {
fs.writeFileSync(filename, JSON.stringify(result, null, 2));
console.log('Created: %s', filename);
}
} catch(ex) {
console.log('ERROR', ex);
}
})();
15 changes: 7 additions & 8 deletions sub/ux/src/web/textbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,21 @@ export class TextBox extends React.Component {
delay: 100
};

static initialState(props) {
return {
value: props.value || ''
}
}

constructor() {
super(...arguments);

this.state = TextBox.initialState(this.props);
this.state = { value: this.props.value || '' };

this._timeout = Async.timeout(this.props.delay);
}

componentWillReceiveProps(nextProps) {
this.state = TextBox.initialState(nextProps);

// Update state when parent is re-rendered (e.g., input is reused across different detail views).
// https://facebook.github.io/react/docs/react-component.html#componentwillreceiveprops
if (nextProps.value != this.props.value) {
this.setState({ value: nextProps.value || '' });
}
}

get value() {
Expand Down

0 comments on commit 3ea9ee1

Please sign in to comment.