Repository navigation
Expand file tree
/
Copy pathGetSignedURLSetAll.m
More file actions
133 lines (124 loc) · 5.63 KB
/
Copy pathGetSignedURLSetAll.m
File metadata and controls
133 lines (124 loc) · 5.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
classdef GetSignedURLSetAll < ndi.cloud.api.call
%GETSIGNEDURLSETALL Walk every page of a document's signed URL set.
%
% Implementation behind ndi.cloud.api.files.getSignedURLSetAll. Calls
% GetSignedURLSet repeatedly, following nextCursor until the server
% reports no more pages, and merges the per-page `files` maps into a
% single struct. A safety cap on the total number of pages prevents an
% accidental unbounded walk.
properties
limit (1,1) double
maxPages (1,1) double
fileSeries (1,1) string
% Which namespace cloudDocumentID is in: "cloud" (mongo _id) or
% "ndi" (data.base.id). See NDI-matlab#968.
idNamespace (1,1) string
end
methods
function this = GetSignedURLSetAll(args)
arguments
args.cloudDatasetID (1,1) string
args.cloudDocumentID (1,1) string
args.limit (1,1) double = 500
args.maxPages (1,1) double = 1000
args.fileSeries (1,1) string = ""
args.idNamespace (1,1) string ...
{mustBeMember(args.idNamespace,["cloud","ndi"])} = "cloud"
end
this.cloudDatasetID = args.cloudDatasetID;
this.cloudDocumentID = args.cloudDocumentID;
this.idNamespace = args.idNamespace;
this.limit = args.limit;
this.maxPages = args.maxPages;
this.fileSeries = args.fileSeries;
if strcmp(this.idNamespace, "ndi")
this.endpointName = 'get_ndi_signed_url_set';
else
this.endpointName = 'get_signed_url_set';
end
end
function [b, answer, apiResponse, apiURL] = execute(this)
b = false;
apiResponse = [];
apiURL = [];
cursor = "";
merged = struct();
merged.datasetId = this.cloudDatasetID;
merged.documentId = this.cloudDocumentID;
% A containers.Map, not a struct: the keys are DID file uids and
% a struct would carry the JSONDECODE-renamed versions of them.
merged.files = containers.Map('KeyType','char','ValueType','any');
merged.fileInfo = ndi.cloud.api.implementation.files.fileMetadataArray([]);
merged.pageCount = 0;
merged.totalCount = 0;
merged.pages = 0;
for pageIdx = 1:this.maxPages
[ok, page, apiResponse, apiURL] = this.fetchPage(cursor);
if ~ok
answer = page;
return;
end
if isstruct(page) && isfield(page, 'files') && isa(page.files,'containers.Map')
pageKeys = keys(page.files);
for i = 1:numel(pageKeys)
merged.files(pageKeys{i}) = page.files(pageKeys{i});
end
merged.pageCount = merged.pageCount + numel(pageKeys);
end
if isstruct(page) && isfield(page, 'fileInfo') && isstruct(page.fileInfo)
merged.fileInfo = [merged.fileInfo; page.fileInfo(:)];
end
if isstruct(page) && isfield(page, 'totalCount')
merged.totalCount = page.totalCount;
end
if isstruct(page) && isfield(page, 'expiresAt')
merged.expiresAt = page.expiresAt;
end
merged.pages = merged.pages + 1;
% Terminate when the server signals no more pages: either
% nextCursor is absent, missing (jsondecode -> []), or an
% empty string.
nextCursor = "";
if isstruct(page) && isfield(page, 'nextCursor') && ~isempty(page.nextCursor)
try
nextCursor = string(page.nextCursor);
catch
nextCursor = "";
end
end
if strlength(nextCursor) == 0
b = true;
answer = merged;
return;
end
if strcmp(nextCursor, cursor)
% The server handed back the cursor just used. Following it
% would page until maxPages for no gain; stop and say why.
error('NDI:CloudApi:SignedURLSet:CursorDidNotAdvance', ...
['The signed URL set cursor did not advance after ' ...
'page %d. Refusing to page in place.'], merged.pages);
end
cursor = nextCursor;
end
% Ran off maxPages without a terminating page. Return partial
% result with a flag so callers see it as a bounded failure
% rather than success.
b = false;
merged.state = 'maxPagesReached';
answer = merged;
end
end
methods (Access = protected)
function [ok, page, apiResponse, apiURL] = fetchPage(this, cursor)
%FETCHPAGE Fetch one page. Seam: a test subclass overrides this to
% script a sequence of pages, so the paging, merging and
% cursor-advance logic can be exercised without a server.
[ok, page, apiResponse, apiURL] = ndi.cloud.api.files.getSignedURLSet(...
this.cloudDatasetID, this.cloudDocumentID, ...
'idNamespace', this.idNamespace, ...
'limit', this.limit, ...
'cursor', cursor, ...
'fileSeries', this.fileSeries);
end
end
end