Skip to content

Commit e295ec2

Browse files
committed
added test for request_hooks, removed null check bug for existing hooks
1 parent 87a649a commit e295ec2

File tree

3 files changed

+125
-13
lines changed

3 files changed

+125
-13
lines changed

dash/dash-renderer/src/actions/api.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ export default function apiThunk(endpoint, method, store, id, body) {
6666
}
6767

6868
if (res.status === STATUS.UNAUTHORIZED) {
69-
console.log(getState());
7069
if (hooks.request_refresh_jwt) {
7170
const body = await res.text();
7271
if (body.includes(JWT_EXPIRED_MESSAGE)) {

dash/dash-renderer/src/actions/callbacks.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ function handleServerside(
308308
config: any,
309309
payload: any
310310
): Promise<any> {
311-
if (hooks.request_pre !== null) {
311+
if (hooks.request_pre) {
312312
hooks.request_pre(payload);
313313
}
314314

@@ -366,7 +366,7 @@ function handleServerside(
366366
if (status === STATUS.OK) {
367367
return res.json().then((data: any) => {
368368
const {multi, response} = data;
369-
if (hooks.request_post !== null) {
369+
if (hooks.request_post) {
370370
hooks.request_post(payload, response);
371371
}
372372

@@ -520,9 +520,9 @@ export function executeCallback(
520520

521521
let newConfig = config;
522522
let newHeaders: Record<string, string> | null = null;
523-
let retry = 0;
523+
let lastError: any;
524524

525-
while (true) {
525+
for (let retry = 0; retry <= MAX_AUTH_RETRIES; retry++) {
526526
try {
527527
const data = await handleServerside(
528528
dispatch,
@@ -537,8 +537,7 @@ export function executeCallback(
537537

538538
return {data, payload};
539539
} catch (res) {
540-
retry++;
541-
540+
lastError = res;
542541
if (
543542
retry <= MAX_AUTH_RETRIES &&
544543
res.status === STATUS.UNAUTHORIZED
@@ -548,12 +547,16 @@ export function executeCallback(
548547
if (body.includes(JWT_EXPIRED_MESSAGE)) {
549548
// From dash embedded
550549
if (hooks.request_refresh_jwt !== null) {
551-
const newJwt =
552-
await hooks.request_refresh_jwt(
550+
let oldJwt = null;
551+
if (config.fetch.headers.Authorization) {
552+
oldJwt =
553553
config.fetch.headers.Authorization.substr(
554554
'Bearer '.length
555-
)
556-
);
555+
);
556+
}
557+
558+
const newJwt =
559+
await hooks.request_refresh_jwt(oldJwt);
557560
if (newJwt) {
558561
newHeaders = {
559562
Authorization: `Bearer ${newJwt}`
@@ -571,10 +574,12 @@ export function executeCallback(
571574
}
572575
}
573576

574-
// here, it is an error we're not supposed to retry.
575-
throw res;
577+
break;
576578
}
577579
}
580+
581+
// we reach here when we run out of retries.
582+
return {error: lastError, payload: null};
578583
} catch (error) {
579584
return {error, payload: null};
580585
}

tests/integration/renderer/test_request_hooks.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import json
2+
import functools
3+
import flask
24

35
from dash import Dash, Output, Input, html, dcc
6+
from werkzeug.exceptions import HTTPException
47

58

69
def test_rdrh001_request_hooks(dash_duo):
@@ -185,3 +188,108 @@ def update_output(value):
185188
assert dash_duo.find_element("#output-post").text == "request_post!!!"
186189

187190
dash_duo.percy_snapshot(name="request-hooks interpolated")
191+
192+
def test_rdrh003_refresh_jwt(dash_duo):
193+
194+
app = Dash(__name__)
195+
196+
app.index_string = """<!DOCTYPE html>
197+
<html>
198+
<head>
199+
{%metas%}
200+
<title>{%title%}</title>
201+
{%favicon%}
202+
{%css%}
203+
</head>
204+
<body>
205+
<div>Testing custom DashRenderer</div>
206+
{%app_entry%}
207+
<footer>
208+
{%config%}
209+
{%scripts%}
210+
<script id="_dash-renderer" type"application/json">
211+
const renderer = new DashRenderer({
212+
request_refresh_jwt: (old_token) => {
213+
console.log("refreshing token", old_token);
214+
var new_token = "." + (old_token || "");
215+
var output = document.getElementById('output-token')
216+
if(output) {
217+
output.innerHTML = new_token;
218+
}
219+
return new_token;
220+
}
221+
})
222+
</script>
223+
</footer>
224+
<div>With request hooks</div>
225+
</body>
226+
</html>"""
227+
228+
229+
app.layout = html.Div(
230+
[
231+
dcc.Input(id="input", value="initial value"),
232+
html.Div(
233+
html.Div(
234+
[
235+
html.Div(id="output-1"),
236+
html.Div(id="output-token")
237+
]
238+
)
239+
),
240+
]
241+
)
242+
243+
@app.callback(Output("output-1", "children"), [Input("input", "value")])
244+
def update_output(value):
245+
return value
246+
247+
required_jwt_len = 0
248+
249+
# test with an auth layer that requires a JWT with a certain length
250+
def protect_route(func):
251+
@functools.wraps(func)
252+
def wrap(*args, **kwargs):
253+
try:
254+
if flask.request.method == 'OPTIONS':
255+
return func(*args, **kwargs)
256+
token = flask.request.authorization or flask.request.headers.environ.get(
257+
"HTTP_AUTHORIZATION"
258+
)
259+
if required_jwt_len and (not token or len(token) != required_jwt_len + len('Bearer ')):
260+
flask.abort(401, description="JWT Expired " + str(token))
261+
except HTTPException as e:
262+
return e
263+
return func(*args, **kwargs)
264+
return wrap
265+
266+
267+
# wrap all API calls with auth.
268+
for name, method in (
269+
(x, app.server.view_functions[x])
270+
for x in app.routes
271+
if x in app.server.view_functions
272+
):
273+
app.server.view_functions[name] = protect_route(method)
274+
275+
dash_duo.start_server(app)
276+
277+
_in = dash_duo.find_element("#input")
278+
dash_duo.clear_input(_in)
279+
280+
required_jwt_len = 1
281+
282+
_in.send_keys("fired request")
283+
284+
dash_duo.wait_for_text_to_equal("#output-1", "fired request")
285+
dash_duo.wait_for_text_to_equal("#output-token", ".")
286+
287+
required_jwt_len = 2
288+
289+
dash_duo.clear_input(_in)
290+
_in.send_keys("fired request again")
291+
292+
dash_duo.wait_for_text_to_equal("#output-1", "fired request again")
293+
dash_duo.wait_for_text_to_equal("#output-token", "..")
294+
295+
dash_duo.percy_snapshot(name="request-hooks jwt-refresh")

0 commit comments

Comments
 (0)