Skip to content

Commit a6f2308

Browse files
committed
WIP Auth2 probe responses for A/V
1 parent 2221286 commit a6f2308

14 files changed

Lines changed: 353 additions & 33 deletions

File tree

src/lib/getServices.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Utils } from 'manifesto.js';
22
import { filterByTypes } from './typeFilters';
3+
import CanvasAttributes from './CanvasAttributes';
34

45
/**
56
*/
@@ -18,7 +19,7 @@ export function getProbeService(resource) {
1819
&& (
1920
Utils.getService(resource, 'http://iiif.io/api/auth/1/probe')
2021
|| filterByTypes(Utils.getServices(resource), 'AuthProbeService2')[0]
21-
);
22+
);
2223
}
2324

2425
/**

src/lib/typeFilters.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,21 @@ export function audioResourcesFrom(resources) {
1717
return filterByTypes(resources, CanvasAttributes.SOUND_TYPES);
1818
}
1919

20+
/**
21+
*/
22+
export function anyImageServices(resource) {
23+
const services = resource ? resource.getServices() : [];
24+
return services.filter(s => CanvasAttributes.IMAGE_SERVICE_PROFILES.includes(s.getProfile()));
25+
}
26+
27+
export function hasImageService(resource) {
28+
const imageServices = anyImageServices(resource);
29+
return imageServices[0] && imageServices[0].id;
30+
}
31+
2032
/** */
2133
export function iiifImageResourcesFrom(resources) {
22-
return filterByTypes(resources, CanvasAttributes.IMAGE_TYPES).filter((r) => {
23-
const services = r ? r.getServices() : [];
24-
const imageServices = services.filter(s => CanvasAttributes.IMAGE_SERVICE_PROFILES.includes(s.getProfile()));
25-
return imageServices[0] && imageServices[0].id;
26-
});
34+
return filterByTypes(resources, CanvasAttributes.IMAGE_TYPES).filter((r) => hasImageService(r));
2735
}
2836

2937
/** */

src/state/actions/action-types.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ const ActionTypes = {
4848
RECEIVE_DEGRADED_INFO_RESPONSE: 'mirador/RECEIVE_DEGRADED_INFO_RESPONSE',
4949
RECEIVE_INFO_RESPONSE_FAILURE: 'mirador/RECEIVE_INFO_RESPONSE_FAILURE',
5050
REMOVE_INFO_RESPONSE: 'mirador/REMOVE_INFO_RESPONSE',
51+
52+
REQUEST_PROBE_RESPONSE: 'mirador/REQUEST_PROBE_RESPONSE',
53+
RECEIVE_PROBE_RESPONSE: 'mirador/RECEIVE_PROBE_RESPONSE',
54+
RECEIVE_DEGRADED_PROBE_RESPONSE: 'mirador/RECEIVE_DEGRADED_PROBE_RESPONSE',
55+
RECEIVE_PROBE_RESPONSE_FAILURE: 'mirador/RECEIVE_PROBE_RESPONSE_FAILURE',
56+
REMOVE_PROBE_RESPONSE: 'mirador/REMOVE_PROBE_RESPONSE',
5157
UPDATE_WORKSPACE_MOSAIC_LAYOUT: 'mirador/UPDATE_WORKSPACE_MOSAIC_LAYOUT',
5258
UPDATE_VIEWPORT: 'mirador/UPDATE_VIEWPORT',
5359
UPDATE_ELASTIC_WINDOW_LAYOUT: 'mirador/UPDATE_ELASTIC_WINDOW_LAYOUT',

src/state/actions/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ export * from './elasticLayout';
1616
export * from './search';
1717
export * from './layers';
1818
export * from './catalog';
19+
export * from './probeResponse';

src/state/actions/probeResponse.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import ActionTypes from './action-types';
2+
import { getProbeService } from '../../lib/getServices';
3+
4+
/**
5+
* requestProbeResponse - action creator
6+
*
7+
* @param {String} infoId
8+
* @memberof ActionCreators
9+
*/
10+
export function requestProbeResponse(probeId, resource, windowId) {
11+
return {
12+
probeId,
13+
resource,
14+
type: ActionTypes.REQUEST_PROBE_RESPONSE,
15+
windowId,
16+
};
17+
}
18+
19+
/**
20+
* receiveProbeResponse - action creator
21+
*
22+
* @param {String} infoId
23+
* @param {Object} manifestJson
24+
* @memberof ActionCreators
25+
*/
26+
export function receiveProbeResponse(probeId, probeJson, ok, tokenServiceId) {
27+
return {
28+
ok,
29+
probeId,
30+
probeJson,
31+
tokenServiceId,
32+
type: ActionTypes.RECEIVE_PROBE_RESPONSE,
33+
};
34+
}
35+
36+
/**
37+
* receiveDegradedProbeResponse - action creator
38+
*
39+
* @param {String} infoId
40+
* @param {Object} manifestJson
41+
* @memberof ActionCreators
42+
*/
43+
export function receiveDegradedProbeResponse(probeId, probeJson, ok, tokenServiceId, windowId) {
44+
return {
45+
ok,
46+
probeId,
47+
probeJson,
48+
tokenServiceId,
49+
type: ActionTypes.RECEIVE_DEGRADED_PROBE_RESPONSE,
50+
windowId,
51+
};
52+
}
53+
54+
/**
55+
* receiveProbeResponseFailure - action creator
56+
*
57+
* @param {String} infoId
58+
* @param {String} error
59+
* @memberof ActionCreators
60+
*/
61+
export function receiveProbeResponseFailure(probeId, error, tokenServiceId) {
62+
return {
63+
error,
64+
probeId,
65+
tokenServiceId,
66+
type: ActionTypes.RECEIVE_PROBE_RESPONSE_FAILURE,
67+
};
68+
}
69+
70+
/**
71+
* fetchProbeResponse - action creator
72+
*
73+
* @param {String} infoId
74+
* @memberof ActionCreators
75+
*/
76+
export function fetchProbeResponse({ resourceId, resource, windowId }) {
77+
const probeService = resource && getProbeService(resource);
78+
const probeId = (resourceId || probeService.id);
79+
return requestProbeResponse(probeId, resource, windowId);
80+
}
81+
82+
/**
83+
* removeProbeResponse - action creator
84+
*
85+
* @param {String} probeId
86+
* @memberof ActionCreators
87+
*/
88+
export function removeProbeResponse(probeId) {
89+
return { probeId, type: ActionTypes.REMOVE_PROBE_RESPONSE };
90+
}

src/state/reducers/accessTokens.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,17 @@ export function accessTokensReducer(state = {}, action) {
4646
if (!action.tokenServiceId) return state;
4747
if (state[action.tokenServiceId].success) return state;
4848

49+
return {
50+
...state,
51+
[action.tokenServiceId]: {
52+
...state[action.tokenServiceId],
53+
success: true,
54+
},
55+
};
56+
case ActionTypes.RECEIVE_PROBE_RESPONSE:
57+
if (!action.tokenServiceId) return state;
58+
if (state[action.tokenServiceId].success) return state;
59+
4960
return {
5061
...state,
5162
[action.tokenServiceId]: {

src/state/reducers/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ export * from './elasticLayout';
1313
export * from './search';
1414
export * from './layers';
1515
export * from './catalog';
16+
export * from './probeResponses';
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import ActionTypes from '../actions/action-types';
2+
3+
/**
4+
* probeResponsesReducer
5+
*/
6+
export const probeResponsesReducer = (state = {}, action) => {
7+
switch (action.type) {
8+
case ActionTypes.REQUEST_PROBE_RESPONSE:
9+
return {
10+
...state,
11+
[action.probeId]: {
12+
id: action.probeId,
13+
isFetching: true,
14+
},
15+
};
16+
case ActionTypes.RECEIVE_PROBE_RESPONSE:
17+
return {
18+
...state,
19+
[action.probeId]: {
20+
degraded: false,
21+
id: action.probeId,
22+
isFetching: false,
23+
json: action.probeJson,
24+
tokenServiceId: action.tokenServiceId,
25+
},
26+
};
27+
case ActionTypes.RECEIVE_DEGRADED_PROBE_RESPONSE:
28+
return {
29+
...state,
30+
[action.probeId]: {
31+
degraded: true,
32+
id: action.probeId,
33+
isFetching: false,
34+
json: action.probeJson,
35+
tokenServiceId: action.tokenServiceId,
36+
},
37+
};
38+
case ActionTypes.RECEIVE_PROBE_RESPONSE_FAILURE:
39+
return {
40+
...state,
41+
[action.probeId]: {
42+
error: action.error,
43+
id: action.probeId,
44+
isFetching: false,
45+
tokenServiceId: action.tokenServiceId,
46+
},
47+
};
48+
case ActionTypes.REMOVE_PROBE_RESPONSE:
49+
return Object.keys(state).reduce((object, key) => {
50+
if (key !== action.probeId) {
51+
object[key] = state[key]; // eslint-disable-line no-param-reassign
52+
}
53+
return object;
54+
}, {});
55+
case ActionTypes.IMPORT_MIRADOR_STATE:
56+
return {};
57+
default: return state;
58+
}
59+
};

src/state/reducers/rootReducer.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
searchesReducer,
1616
layersReducer,
1717
catalogReducer,
18+
probeResponsesReducer,
1819
} from '.';
1920

2021
/**
@@ -35,6 +36,7 @@ export default function createRootReducer(pluginReducers) {
3536
infoResponses: infoResponsesReducer,
3637
layers: layersReducer,
3738
manifests: manifestsReducer,
39+
probeResponses: probeResponsesReducer,
3840
searches: searchesReducer,
3941
viewers: viewersReducer,
4042
windows: windowsReducer,

src/state/sagas/auth.js

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ import {
1414
} from '../actions';
1515
import {
1616
selectInfoResponses,
17+
selectProbeResponses,
1718
getVisibleCanvases,
1819
getWindows,
1920
getConfig,
2021
getAuth,
2122
getAccessTokens,
2223
} from '../selectors';
23-
import { fetchInfoResponse } from './iiif';
24+
import { fetchInfoResponse, fetchProbeResponse } from './iiif';
2425

2526
/** */
2627
export function* refetchInfoResponsesOnLogout({ tokenServiceId }) {
@@ -70,6 +71,54 @@ export function* refetchInfoResponses({ serviceId }) {
7071
}));
7172
}
7273

74+
/** */
75+
export function* refetchProbeResponsesOnLogout({ tokenServiceId }) {
76+
// delay logout actions to give the cookie service a chance to invalidate our cookies
77+
// before we reinitialize openseadragon and rerequest images.
78+
79+
yield delay(2000);
80+
yield call(refetchProbeResponses, { serviceId: tokenServiceId });
81+
}
82+
83+
/**
84+
* Figure out what info responses could have used the access token service and:
85+
* - refetch, if they are currently visible
86+
* - throw them out (and lazy re-fetch) otherwise
87+
*/
88+
export function* refetchProbeResponses({ serviceId }) {
89+
const windows = yield select(getWindows);
90+
91+
const canvases = yield all(
92+
Object.keys(windows).map(windowId => select(getVisibleCanvases, { windowId })),
93+
);
94+
95+
const visibleProbeServiceIds = flatten(flatten(canvases).map((canvas) => {
96+
const miradorCanvas = new MiradorCanvas(canvas);
97+
return miradorCanvas.imageResources.filter((r) => getProbeService(r)).map((r) => getProbeService(r));
98+
}));
99+
100+
const probeResponses = yield select(selectProbeResponses);
101+
/** */
102+
const haveThisTokenService = probeResponse => {
103+
const services = Utils.getServices(probeResponse);
104+
return services.some(e => {
105+
const probeTokenService = getTokenService(e);
106+
return probeTokenService && probeTokenService.id === serviceId;
107+
});
108+
};
109+
110+
const obsoleteProbeResponses = Object.values(probeResponses).filter(
111+
i => i.json && haveThisTokenService(i.json),
112+
);
113+
114+
yield all(obsoleteProbeResponses.map(({ id: probeId }) => {
115+
if (visibleProbeServiceIds.includes(probeId)) {
116+
return call(fetchProbeResponse, { probeId });
117+
}
118+
return put({ probeId, type: ActionTypes.REMOVE_PROBE_RESPONSE });
119+
}));
120+
}
121+
73122
/** try to start any non-interactive auth flows */
74123
export function* doAuthWorkflow({ infoJson, windowId }) {
75124
const auths = yield select(getAuth);
@@ -152,9 +201,13 @@ export function* invalidateInvalidAuth({ serviceId }) {
152201
export default function* authSaga() {
153202
yield all([
154203
takeEvery(ActionTypes.RECEIVE_DEGRADED_INFO_RESPONSE, rerequestOnAccessTokenFailure),
204+
takeEvery(ActionTypes.RECEIVE_DEGRADED_PROBE_RESPONSE, rerequestOnAccessTokenFailure),
155205
takeEvery(ActionTypes.RECEIVE_ACCESS_TOKEN_FAILURE, invalidateInvalidAuth),
156206
takeEvery(ActionTypes.RECEIVE_DEGRADED_INFO_RESPONSE, doAuthWorkflow),
207+
takeEvery(ActionTypes.RECEIVE_DEGRADED_PROBE_RESPONSE, doAuthWorkflow),
157208
takeEvery(ActionTypes.RECEIVE_ACCESS_TOKEN, refetchInfoResponses),
209+
takeEvery(ActionTypes.RECEIVE_ACCESS_TOKEN, refetchProbeResponses),
158210
takeEvery(ActionTypes.RESET_AUTHENTICATION_STATE, refetchInfoResponsesOnLogout),
211+
takeEvery(ActionTypes.RESET_AUTHENTICATION_STATE, refetchProbeResponsesOnLogout),
159212
]);
160213
}

0 commit comments

Comments
 (0)