Skip to content

Commit

Permalink
🐛🔍 Fix querying to work with Annotation classes (#56)
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-evans committed Jun 25, 2018
1 parent aee83e0 commit 6d7601a
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 60 deletions.
4 changes: 2 additions & 2 deletions packages/@atjson/document/src/attributes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function unprefix(vendorPrefix: string, attribute: Attribute): Attribute
return null;
} else if (typeof attribute === 'object') {
return Object.keys(attribute).reduce((attrs: Attributes, key: string) => {
let value = attrs[key];
let value = attribute[key];
if (key.indexOf(`-${vendorPrefix}-`) === 0 && value !== undefined) {
attrs[key.slice(`-${vendorPrefix}-`.length)] = unprefix(vendorPrefix, value);
}
Expand All @@ -42,7 +42,7 @@ export function toJSON(vendorPrefix: string, attribute: Attribute): JSON {
return Object.keys(attribute).reduce((copy: JSONObject, key: string) => {
let value = attribute[key];
if (value !== undefined) {
copy[key] = toJSON(vendorPrefix, value);
copy[`-${vendorPrefix}-${key}`] = toJSON(vendorPrefix, value);
}
return copy;
}, {});
Expand Down
9 changes: 7 additions & 2 deletions packages/@atjson/document/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export {

export type Schema<T extends Annotation> = T[];

export type Schema<T extends Annotation> = T[];

export default class AtJSON {
static contentType: string;
static schema: Schema<any>;
Expand Down Expand Up @@ -93,11 +95,14 @@ export default class AtJSON {
}
}

replaceAnnotation(annotation: Annotation, ...newAnnotations: Annotation[]): void {
replaceAnnotation(annotation: Annotation, ...newAnnotations: AnnotationJSON[]): Annotation[] {
let index = this.annotations.indexOf(annotation);
if (index > -1) {
this.annotations.splice(index, 1, ...newAnnotations);
let annotations = newAnnotations.map(json => this.createAnnotation(json));
this.annotations.splice(index, 1, ...annotations);
return annotations;
}
return [];
}

insertText(start: number, text: string, behaviour: AdjacentBoundaryBehaviour = AdjacentBoundaryBehaviour.default) {
Expand Down
56 changes: 23 additions & 33 deletions packages/@atjson/document/src/query.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Annotation from './annotation';
import Document from './index';
import Document, { AnnotationJSON } from './index';

export interface Filter {
[key: string]: any;
Expand All @@ -13,7 +13,7 @@ export interface FlattenedRenaming {
[key: string]: string;
}

export type Transform = (annotation: Annotation) => Annotation | null;
export type Transform = (annotation: AnnotationJSON) => AnnotationJSON | AnnotationJSON[] | null;

export function flatten(array: any[]): any[] {
let flattenedArray = [];
Expand All @@ -28,10 +28,6 @@ export function flatten(array: any[]): any[] {
return flattenedArray;
}

function clone(object: any) {
return JSON.parse(JSON.stringify(object));
}

function without(object: any, attributes: string[]): any {
let copy: { [key: string]: any } = {};
Object.keys(object).forEach((key: string) => {
Expand Down Expand Up @@ -108,15 +104,15 @@ export default class Query {
constructor(document: Document, filter: Filter) {
this.document = document;
this.filter = filter;
this.transforms = [(annotation: Annotation) => annotation];
this.currentAnnotations = document.annotations.filter(annotation => matches(annotation, this.filter));
this.transforms = [(annotation: AnnotationJSON) => annotation];
this.currentAnnotations = document.annotations.filter(annotation => matches(annotation.toJSON(), this.filter));
}

run(newAnnotation: Annotation): Annotation[] {
run(newAnnotation: AnnotationJSON): AnnotationJSON[] {
// Release the list of currently filtered annotations
this.currentAnnotations = [];
if (matches(newAnnotation, this.filter)) {
let alteredAnnotations = this.transforms.reduce((annotations: Annotation[], transform: Transform) => {
let alteredAnnotations = this.transforms.reduce((annotations: AnnotationJSON[], transform: Transform) => {
return flatten(annotations.map(transform));
}, [newAnnotation]);

Expand All @@ -127,8 +123,7 @@ export default class Query {

set(patch: any): Query {
let flattenedPatch = flattenPropertyPaths(patch, { keys: true });
return this.map((annotation: Annotation) => {
let result = clone(annotation);
return this.map((result: AnnotationJSON) => {
Object.keys(flattenedPatch).forEach(key => {
set(result, key, flattenedPatch[key]);
});
Expand All @@ -137,14 +132,14 @@ export default class Query {
}

unset(...keys: string[]): Query {
return this.map((annotation: Annotation) => {
return this.map((annotation: AnnotationJSON) => {
return without(annotation, keys);
});
}

rename(renaming: Renaming): Query {
let flattenedRenaming = flattenPropertyPaths(renaming, { keys: true, values: true });
return this.map((annotation: Annotation) => {
return this.map((annotation: AnnotationJSON) => {
let result = without(annotation, Object.keys(flattenedRenaming));
Object.keys(flattenedRenaming).forEach(key => {
let value = get(annotation, key);
Expand All @@ -154,27 +149,22 @@ export default class Query {
});
}

map(mapping: Renaming | Transform): Query {
if (typeof mapping === 'object') {
return this.rename(mapping);
} else {
this.transforms.push(mapping);
this.currentAnnotations = flatten(this.currentAnnotations.map(annotation => {
let alteredAnnotation = mapping(annotation);
if (alteredAnnotation == null) {
this.document.removeAnnotation(annotation);
} else if (Array.isArray(alteredAnnotation)) {
this.document.replaceAnnotation(annotation, ...alteredAnnotation);
} else {
this.document.replaceAnnotation(annotation, alteredAnnotation);
}
return alteredAnnotation;
}));
return this;
}
map(mapping: Transform): Query {
this.transforms.push(mapping);
this.currentAnnotations = flatten(this.currentAnnotations.map((annotation: Annotation) => {
let alteredAnnotation = mapping(annotation.toJSON());
if (alteredAnnotation == null) {
return this.document.replaceAnnotation(annotation);
} else if (Array.isArray(alteredAnnotation)) {
return this.document.replaceAnnotation(annotation, ...alteredAnnotation);
} else {
return this.document.replaceAnnotation(annotation, alteredAnnotation);
}
}));
return this;
}

remove(): Query {
return this.map((_: Annotation) => null);
return this.map(_ => null);
}
}
30 changes: 25 additions & 5 deletions packages/@atjson/document/test/atjson-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describe('new Document', () => {

describe('slice', () => {
let document = new TestSource({
content: 'Hello, world!',
content: 'Hello, world!\n\uFFFC',
annotations: [{
type: '-test-bold',
start: 0,
Expand All @@ -38,15 +38,21 @@ describe('new Document', () => {
start: 0,
end: 13,
attributes: {}
}, {
type: '-test-instagram',
start: 14,
end: 15,
attributes: {
'-test-uri': 'https://www.instagram.com/p/BeW0pqZDUuK/'
}
}]
});

test('source documents are unaltered', () => {
let doc = document.slice(1, 13);
expect(doc.content).toBe('ello, world!');
let doc = document.slice(1, 15);

expect(doc.toJSON()).toEqual({
content: 'ello, world!',
content: 'ello, world!\n\uFFFC',
contentType: 'application/vnd.atjson+test',
schema: ['-test-bold', '-test-instagram', '-test-italic', '-test-manual'],
annotations: [{
Expand All @@ -64,11 +70,18 @@ describe('new Document', () => {
start: 0,
end: 12,
attributes: {}
}, {
type: '-test-instagram',
start: 13,
end: 14,
attributes: {
'-test-uri': 'https://www.instagram.com/p/BeW0pqZDUuK/'
}
}]
});

expect(document.toJSON()).toEqual({
content: 'Hello, world!',
content: 'Hello, world!\n\uFFFC',
contentType: 'application/vnd.atjson+test',
schema: ['-test-bold', '-test-instagram', '-test-italic', '-test-manual'],
annotations: [{
Expand All @@ -86,6 +99,13 @@ describe('new Document', () => {
start: 0,
end: 13,
attributes: {}
}, {
type: '-test-instagram',
start: 14,
end: 15,
attributes: {
'-test-uri': 'https://www.instagram.com/p/BeW0pqZDUuK/'
}
}]
});
});
Expand Down
39 changes: 21 additions & 18 deletions packages/@atjson/document/test/query-test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AnnotationJSON } from '../src';
import TestSource from './test-source';

describe.skip('Document.where', () => {
describe('Document.where', () => {
it('runs queries against existing annotations', () => {
let doc = new TestSource({
content: 'Hello',
Expand All @@ -11,7 +11,7 @@ describe.skip('Document.where', () => {
end: 5,
attributes: {}
}, {
type: '-test-emphasis',
type: '-test-em',
start: 0,
end: 5,
attributes: {}
Expand Down Expand Up @@ -74,18 +74,18 @@ describe.skip('Document.where', () => {
annotations: []
});

doc.where({ type: '-test-h1' }).set({ type: '-test-heading', attributes: { level: 1 } });
doc.where({ type: '-test-h1' }).set({ type: '-test-heading', attributes: { '-test-level': 1 } });
doc.addAnnotations({
type: 'h1',
type: '-test-h1',
start: 0,
end: 5,
attributes: {}
});
expect(doc.content).toBe('Hello');
expect(doc.annotations).toEqual([{
type: 'heading',
expect(doc.annotations.map(a => a.toJSON())).toEqual([{
type: '-test-heading',
attributes: {
level: 1
'-test-level': 1
},
start: 0,
end: 5
Expand Down Expand Up @@ -138,9 +138,9 @@ describe.skip('Document.where', () => {
let doc = new TestSource({
content: 'Conde Nast',
annotations: [{
type: 'a',
type: '-test-a',
attributes: {
href: 'https://example.com'
'-test-href': 'https://example.com'
},
start: 0,
end: 5
Expand Down Expand Up @@ -188,12 +188,13 @@ describe.skip('Document.where', () => {
});

doc.where({ type: '-test-a' }).map((annotation: AnnotationJSON) => {
let href = annotation.attributes['-test-href'] as string;
return {
type: '-test-link',
start: annotation.start,
end: annotation.end,
attributes: {
'-test-url': annotation.attributes['-test-href'].replace('http://', 'https://')
'-test-url': href.replace('http://', 'https://')
}
};
});
Expand All @@ -206,7 +207,7 @@ describe.skip('Document.where', () => {
end: 10
});
expect(doc.content).toBe('Conde Nast');
expect(doc.annotations).toEqual([{
expect(doc.annotations.map(a => a.toJSON())).toEqual([{
type: '-test-link',
attributes: {
'-test-url': 'https://example.com'
Expand Down Expand Up @@ -259,7 +260,7 @@ describe.skip('Document.where', () => {
}]
});

doc.where({ type: 'code' }).map((annotation: AnnotationJSON) => {
doc.where({ type: '-test-code' }).map((annotation: AnnotationJSON) => {
return [{
type: '-test-pre',
start: annotation.start,
Expand All @@ -284,7 +285,7 @@ describe.skip('Document.where', () => {
});

expect(doc.content).toBe('string.trim();\nstring.strip');
expect(doc.annotations).toEqual([{
expect(doc.annotations.map(a => a.toJSON())).toEqual([{
type: '-test-pre',
start: 0,
end: 14,
Expand All @@ -304,7 +305,7 @@ describe.skip('Document.where', () => {
'-test-language': 'rb'
}
}, {
type: 'code',
type: '-test-code',
start: 16,
end: 28,
attributes: {}
Expand All @@ -315,7 +316,7 @@ describe.skip('Document.where', () => {
let doc = new TestSource({
content: 'This is ~my caption~\nNext paragraph',
annotations: [{
type: 'photo',
type: '-test-photo',
start: 0,
end: 20,
attributes: {}
Expand All @@ -331,14 +332,16 @@ describe.skip('Document.where', () => {
attributes: {}
});
expect(caption.content).toBe('This is ~my caption~');
expect(doc.annotations).toEqual([{
expect(caption.annotations.map(a => a.toJSON())).toEqual([{
type: '-test-photo',
start: 0,
end: 20
end: 20,
attributes: {}
}, {
type: '-test-italic',
start: 0,
end: 4
end: 4,
attributes: {}
}]);
});
});

0 comments on commit 6d7601a

Please sign in to comment.