Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
coopwire-hypothesis
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
孙灵跃 Leon Sun
coopwire-hypothesis
Commits
30f534ad
Commit
30f534ad
authored
May 27, 2020
by
Lyza Danger Gardner
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add `loadThread` method to `loadAnnotationsService`
parent
737f919e
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
242 additions
and
1 deletion
+242
-1
load-annotations.js
src/sidebar/services/load-annotations.js
+51
-0
load-annotations-test.js
src/sidebar/services/test/load-annotations-test.js
+191
-1
No files found.
src/sidebar/services/load-annotations.js
View file @
30f534ad
...
...
@@ -3,6 +3,8 @@
*/
import
SearchClient
from
'../search-client'
;
import
{
isReply
}
from
'../util/annotation-metadata'
;
// @ngInject
export
default
function
loadAnnotationsService
(
api
,
...
...
@@ -61,7 +63,56 @@ export default function loadAnnotationsService(
searchClient
.
get
({
uri
:
uris
,
group
:
groupId
});
}
/**
* Fetch all annotations in the same thread as `id` and add them to the store.
*
* @param {string} id - Annotation ID. This may be an annotation or a reply.
* @return Promise<Annotation[]> - The annotation, followed by any replies.
*/
async
function
loadThread
(
id
)
{
let
annotation
;
let
replySearchResult
;
// Clear out any annotations already in the store before fetching new ones
store
.
clearAnnotations
();
try
{
store
.
annotationFetchStarted
();
// 1. Fetch the annotation indicated by `id` — the target annotation
annotation
=
await
api
.
annotation
.
get
({
id
});
// 2. If annotation is not the top-level annotation in its thread,
// fetch the top-level annotation
if
(
isReply
(
annotation
))
{
annotation
=
await
api
.
annotation
.
get
({
id
:
annotation
.
references
[
0
]
});
}
// 3. Fetch all of the annotations in the thread, based on the
// top-level annotation
replySearchResult
=
await
api
.
search
({
references
:
annotation
.
id
});
}
finally
{
store
.
annotationFetchFinished
();
}
const
threadAnnotations
=
[
annotation
,
...
replySearchResult
.
rows
];
store
.
addAnnotations
(
threadAnnotations
);
// If we've been successful in retrieving a thread, with a top-level annotation,
// configure the connection to the real-time update service to send us
// updates to any of the annotations in the thread.
if
(
!
isReply
(
annotation
))
{
streamFilter
.
addClause
(
'/references'
,
'one_of'
,
annotation
.
id
,
true
)
.
addClause
(
'/id'
,
'equals'
,
annotation
.
id
,
true
);
streamer
.
setConfig
(
'filter'
,
{
filter
:
streamFilter
.
getFilter
()
});
streamer
.
connect
();
}
return
threadAnnotations
;
}
return
{
load
,
loadThread
,
};
}
src/sidebar/services/test/load-annotations-test.js
View file @
30f534ad
...
...
@@ -43,13 +43,17 @@ describe('loadAnnotationsService', () => {
longRunningSearchClient
=
false
;
fakeApi
=
{
search
:
sinon
.
stub
(),
search
:
sinon
.
stub
().
returns
({
rows
:
[]
}),
annotation
:
{
get
:
sinon
.
stub
(),
},
};
fakeStore
=
{
addAnnotations
:
sinon
.
stub
(),
annotationFetchFinished
:
sinon
.
stub
(),
annotationFetchStarted
:
sinon
.
stub
(),
clearAnnotations
:
sinon
.
stub
(),
frames
:
sinon
.
stub
(),
removeAnnotations
:
sinon
.
stub
(),
savedAnnotations
:
sinon
.
stub
(),
...
...
@@ -62,6 +66,9 @@ describe('loadAnnotationsService', () => {
reconnect
:
sinon
.
stub
(),
};
fakeStreamFilter
=
{
addClause
:
sinon
.
stub
().
returns
({
addClause
:
sinon
.
stub
(),
}),
resetFilter
:
sinon
.
stub
().
returns
({
addClause
:
sinon
.
stub
(),
}),
...
...
@@ -257,4 +264,187 @@ describe('loadAnnotationsService', () => {
assert
.
calledWith
(
console
.
error
,
error
);
});
});
describe
(
'loadThread'
,
()
=>
{
let
threadAnnotations
=
[
{
id
:
'parent_annotation_1'
},
{
id
:
'parent_annotation_2'
,
references
:
[
'parent_annotation_1'
]
},
{
id
:
'target_annotation'
,
references
:
[
'parent_annotation_1'
,
'parent_annotation_2'
],
},
];
it
(
'clears annotations from the store first'
,
()
=>
{
const
svc
=
createService
();
svc
.
loadThread
(
'target_annotation'
);
assert
.
calledOnce
(
fakeStore
.
clearAnnotations
);
});
describe
(
'fetching the target annotation'
,
()
=>
{
beforeEach
(()
=>
{
fakeApi
.
annotation
.
get
.
onFirstCall
().
resolves
({
id
:
'target_annotation'
,
references
:
[],
});
});
it
(
'fetches annotation with given `id`'
,
async
()
=>
{
const
svc
=
createService
();
await
svc
.
loadThread
(
'target_annotation'
);
assert
.
calledWith
(
fakeApi
.
annotation
.
get
,
sinon
.
match
({
id
:
'target_annotation'
})
);
});
it
(
'records the start and end of annotation fetch with the store'
,
async
()
=>
{
const
svc
=
createService
();
await
svc
.
loadThread
(
'target_annotation'
);
assert
.
calledOnce
(
fakeStore
.
annotationFetchStarted
);
assert
.
calledOnce
(
fakeStore
.
annotationFetchFinished
);
});
it
(
'stops the annotation fetch with the store on error'
,
async
()
=>
{
fakeApi
.
annotation
.
get
.
onFirstCall
().
throws
();
const
svc
=
createService
();
try
{
await
svc
.
loadThread
(
'target_annotation'
);
}
catch
(
e
)
{
assert
.
calledOnce
(
fakeStore
.
annotationFetchStarted
);
assert
.
calledOnce
(
fakeStore
.
annotationFetchFinished
);
}
});
});
describe
(
'fetching top-level annotation in thread'
,
()
=>
{
beforeEach
(()
=>
{
fakeApi
.
annotation
.
get
.
onFirstCall
().
resolves
({
id
:
'target_annotation'
,
references
:
[
'parent_annotation_1'
,
'parent_annotation_2'
],
});
fakeApi
.
annotation
.
get
.
onSecondCall
().
resolves
({
id
:
'parent_annotation_1'
,
references
:
[],
});
});
it
(
'fetches top-level annotation'
,
async
()
=>
{
const
svc
=
createService
();
await
svc
.
loadThread
(
'target_annotation'
);
assert
.
calledWith
(
fakeApi
.
annotation
.
get
,
sinon
.
match
({
id
:
'parent_annotation_1'
})
);
});
});
describe
(
'fetching other annotations in the thread'
,
()
=>
{
beforeEach
(()
=>
{
fakeApi
.
annotation
.
get
.
onFirstCall
().
resolves
({
id
:
'target_annotation'
,
references
:
[
'parent_annotation_1'
,
'parent_annotation_2'
],
});
fakeApi
.
annotation
.
get
.
onSecondCall
().
resolves
({
id
:
'parent_annotation_1'
,
references
:
[],
});
fakeApi
.
search
.
resolves
({
rows
:
[
threadAnnotations
[
1
],
threadAnnotations
[
2
]],
});
});
it
(
'retrieves all annotations in the thread'
,
async
()
=>
{
const
svc
=
createService
();
await
svc
.
loadThread
(
'target_annotation'
);
assert
.
calledWith
(
fakeApi
.
search
,
sinon
.
match
({
references
:
'parent_annotation_1'
})
);
});
it
(
'adds all of the annotations in the thread to the store'
,
async
()
=>
{
const
svc
=
createService
();
await
svc
.
loadThread
(
'target_annotation'
);
assert
.
calledWith
(
fakeStore
.
addAnnotations
,
sinon
.
match
.
array
);
});
it
(
'returns thread annotations'
,
async
()
=>
{
const
svc
=
createService
();
const
annots
=
await
svc
.
loadThread
(
'target_annotation'
);
assert
.
equal
(
annots
[
0
].
id
,
'parent_annotation_1'
);
assert
.
equal
(
annots
[
1
].
id
,
'parent_annotation_2'
);
assert
.
equal
(
annots
[
2
].
id
,
'target_annotation'
);
});
});
describe
(
'connecting to streamer for thread updates'
,
()
=>
{
beforeEach
(()
=>
{
fakeApi
.
annotation
.
get
.
onFirstCall
().
resolves
({
id
:
'target_annotation'
,
references
:
[
'parent_annotation_1'
,
'parent_annotation_2'
],
});
fakeApi
.
annotation
.
get
.
onSecondCall
().
resolves
({
id
:
'parent_annotation_1'
,
references
:
[],
});
fakeApi
.
search
.
resolves
({
rows
:
[
threadAnnotations
[
1
],
threadAnnotations
[
2
]],
});
});
it
(
'does not connect to the streamer if no top-level annotation available'
,
async
()
=>
{
// Make it so the "top-level" annotation isn't really top level: it has references
// and so is a reply
fakeApi
.
annotation
.
get
.
onSecondCall
().
resolves
({
id
:
'parent_annotation_1'
,
references
:
[
'something_else'
],
});
const
svc
=
createService
();
svc
.
loadThread
(
'target_annotation'
);
await
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
0
));
assert
.
notCalled
(
fakeStreamer
.
connect
);
});
it
(
'configures the stream filter for changes to the thread'
,
async
()
=>
{
const
fakeAddClause
=
sinon
.
stub
();
fakeStreamFilter
.
addClause
.
returns
({
addClause
:
fakeAddClause
});
fakeStreamFilter
.
getFilter
.
returns
(
'filter'
);
const
svc
=
createService
();
svc
.
loadThread
(
'target_annotation'
);
await
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
0
));
assert
.
calledWith
(
fakeStreamFilter
.
addClause
,
'/references'
,
'one_of'
,
'parent_annotation_1'
,
true
);
assert
.
calledWith
(
fakeAddClause
,
'/id'
,
'equals'
,
'parent_annotation_1'
,
true
);
assert
.
calledWith
(
fakeStreamer
.
setConfig
,
'filter'
,
sinon
.
match
({
filter
:
'filter'
})
);
assert
.
calledOnce
(
fakeStreamer
.
connect
);
});
});
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment