This is an automated email from the ASF dual-hosted git repository.
kaxilnaik pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 068f3fce84e Feature/edge maintenance plugin beautification (#55348)
068f3fce84e is described below
commit 068f3fce84e1d75752a852ba714e12d8f33f6f4f
Author: Jens Scheffler <[email protected]>
AuthorDate: Tue Sep 9 00:13:54 2025 +0200
Feature/edge maintenance plugin beautification (#55348)
@dheerajturaga somehow I was not able to pugh to your repo directly git
gave me an error :-(
```
error: Authentication error: Authentication required: You must have push
access to verify locks
error: failed to push some refs to 'github.com:dheerajturaga/airflow.git'
```
...anyway, another way around, openeing a draft PR here, can you
cherry-pick the commits down to your PR?
The last commit [Rework maintenance dialog to be a
dialog](https://github.com/apache/airflow/commit/7e6d13cf316711f286e3262fa8552b2d62ede965)
has a problem though: Dialog is not opening, it makes a JS script error which
is not much helpful:
```
Uncaught TypeError: can't access property "flushSync", ne is undefined
R http://localhost:28080/edge_worker/static/main.umd.cjs:16
```
---
airflow-core/src/airflow/ui/src/main.tsx | 4 +-
.../providers/edge3/plugins/www/dist/main.umd.cjs | 51 ++++---
.../www/src/components/MaintenanceEnterButton.tsx | 101 ++++++++++++++
.../www/src/components/MaintenanceExitButton.tsx | 61 +++++++++
.../plugins/www/src/components/OperationsCell.tsx | 147 ---------------------
.../www/src/components/WorkerOperations.tsx | 65 +++++++++
.../components/ui/{index.ts => createToaster.ts} | 6 +-
.../edge3/plugins/www/src/components/ui/index.ts | 1 +
.../edge3/plugins/www/src/pages/WorkerPage.tsx | 54 +-------
.../providers/edge3/plugins/www/vite.config.ts | 1 +
.../providers/edge3/worker_api/routes/ui.py | 12 +-
providers/edge3/www-hash.txt | 2 +-
12 files changed, 282 insertions(+), 223 deletions(-)
diff --git a/airflow-core/src/airflow/ui/src/main.tsx
b/airflow-core/src/airflow/ui/src/main.tsx
index 2768d84809a..41f5d1bd0f6 100644
--- a/airflow-core/src/airflow/ui/src/main.tsx
+++ b/airflow-core/src/airflow/ui/src/main.tsx
@@ -21,6 +21,7 @@ import { QueryClientProvider } from "@tanstack/react-query";
import axios, { type AxiosError } from "axios";
import { StrictMode } from "react";
import React from "react";
+import * as ReactDOM from "react-dom";
import { createRoot } from "react-dom/client";
import { I18nextProvider } from "react-i18next";
import { RouterProvider } from "react-router-dom";
@@ -37,10 +38,11 @@ import { client } from "./queryClient";
import { system } from "./theme";
import { clearToken, tokenHandler } from "./utils/tokenHandler";
-// Set React and ReactJSXRuntime on globalThis to share them with the
dynamically imported React plugins.
+// Set React, ReactDOM, and ReactJSXRuntime on globalThis to share them with
the dynamically imported React plugins.
// Only one instance of React should be used.
// Reflect will avoid type checking.
Reflect.set(globalThis, "React", React);
+Reflect.set(globalThis, "ReactDOM", ReactDOM);
Reflect.set(globalThis, "ReactJSXRuntime", ReactJSXRuntime);
// redirect to login page if the API responds with unauthorized or forbidden
errors
diff --git
a/providers/edge3/src/airflow/providers/edge3/plugins/www/dist/main.umd.cjs
b/providers/edge3/src/airflow/providers/edge3/plugins/www/dist/main.umd.cjs
index 871159e89c8..766376f7f11 100644
--- a/providers/edge3/src/airflow/providers/edge3/plugins/www/dist/main.umd.cjs
+++ b/providers/edge3/src/airflow/providers/edge3/plugins/www/dist/main.umd.cjs
@@ -1,4 +1,4 @@
-(function(T,Y){typeof exports=="object"&&typeof
module<"u"?module.exports=Y(require("react"),require("react-dom")):typeof
define=="function"&&define.amd?define(["react","react-dom"],Y):(T=typeof
globalThis<"u"?globalThis:T||self,T.AirflowPlugin=Y(T.React))})(this,function(T){"use
strict";var ZT=Object.defineProperty;var uv=T=>{throw TypeError(T)};var
eN=(T,Y,w)=>Y in
T?ZT(T,Y,{enumerable:!0,configurable:!0,writable:!0,value:w}):T[Y]=w;var
Ke=(T,Y,w)=>eN(T,typeof Y!="symbol"?Y+"":Y,w),Vl= [...]
+(function(E,te){typeof exports=="object"&&typeof
module<"u"?module.exports=te(require("react"),require("react-dom")):typeof
define=="function"&&define.amd?define(["react","react-dom"],te):(E=typeof
globalThis<"u"?globalThis:E||self,E.AirflowPlugin=te(E.React,E.ReactDOM))})(this,function(E,te){"use
strict";var YA=Object.defineProperty;var hb=E=>{throw TypeError(E)};var
QA=(E,te,ve)=>te in
E?YA(E,te,{enumerable:!0,configurable:!0,writable:!0,value:ve}):E[te]=ve;var
Je=(E,te,ve)=>QA(E,typeo [...]
* @license React
* react-jsx-runtime.production.min.js
*
@@ -6,27 +6,27 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
- */var
pv=T,mv=Symbol.for("react.element"),vv=Symbol.for("react.fragment"),bv=Object.prototype.hasOwnProperty,yv=pv.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,xv={key:!0,ref:!0,__self:!0,__source:!0};function
Wl(e,t,n){var r,i={},o=null,s=null;n!==void 0&&(o=""+n),t.key!==void
0&&(o=""+t.key),t.ref!==void 0&&(s=t.ref);for(r in
t)bv.call(t,r)&&!xv.hasOwnProperty(r)&&(i[r]=t[r]);if(e&&e.defaultProps)for(r
in t=e.defaultProps,t)i[r]===void 0&&(i[r]=t[r]);return{$$t [...]
+ */var
vb=E,bb=Symbol.for("react.element"),yb=Symbol.for("react.fragment"),xb=Object.prototype.hasOwnProperty,Cb=vb.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,Sb={key:!0,ref:!0,__self:!0,__source:!0};function
kc(e,t,n){var r,i={},o=null,s=null;n!==void 0&&(o=""+n),t.key!==void
0&&(o=""+t.key),t.ref!==void 0&&(s=t.ref);for(r in
t)xb.call(t,r)&&!Sb.hasOwnProperty(r)&&(i[r]=t[r]);if(e&&e.defaultProps)for(r
in t=e.defaultProps,t)i[r]===void 0&&(i[r]=t[r]);return{$$t [...]
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
- */var xe=typeof
Symbol=="function"&&Symbol.for,Os=xe?Symbol.for("react.element"):60103,Is=xe?Symbol.for("react.portal"):60106,$i=xe?Symbol.for("react.fragment"):60107,Bi=xe?Symbol.for("react.strict_mode"):60108,ji=xe?Symbol.for("react.profiler"):60114,Wi=xe?Symbol.for("react.provider"):60109,Hi=xe?Symbol.for("react.context"):60110,Rs=xe?Symbol.for("react.async_mode"):60111,Ui=xe?Symbol.for("react.concurrent_mode"):60111,Gi=xe?Symbol.for("react.forward_ref"):60112,qi=xe?Symbol.for("react
[...]
+ */var ke=typeof
Symbol=="function"&&Symbol.for,Js=ke?Symbol.for("react.element"):60103,Zs=ke?Symbol.for("react.portal"):60106,Zi=ke?Symbol.for("react.fragment"):60107,eo=ke?Symbol.for("react.strict_mode"):60108,to=ke?Symbol.for("react.profiler"):60114,no=ke?Symbol.for("react.provider"):60109,ro=ke?Symbol.for("react.context"):60110,ea=ke?Symbol.for("react.async_mode"):60111,io=ke?Symbol.for("react.concurrent_mode"):60111,oo=ke?Symbol.for("react.forward_ref"):60112,so=ke?Symbol.for("react
[...]
<svg width="46" height="15" style="left: -15.5px; position: absolute;
top: 0; filter: drop-shadow(rgba(0, 0, 0, 0.4) 0px 1px 1.1px);">
<g transform="translate(2 3)">
<path fill-rule="evenodd" d="M 15 4.5L 15 2L 11.5 5.5L 15 9L 15 6.5L
31 6.5L 31 9L 34.5 5.5L 31 2L 31 4.5Z" style="stroke-width: 2px; stroke:
white;"></path>
<path fill-rule="evenodd" d="M 15 4.5L 15 2L 11.5 5.5L 15 9L 15 6.5L
31 6.5L 31 9L 34.5 5.5L 31 2L 31 4.5Z"></path>
</g>
- </svg>`,n.body.appendChild(r)};function
MS(e){if(!(!e||e.ownerDocument.activeElement!==e))try{const{selectionStart:t,selectionEnd:n,value:r}=e,i=r.substring(0,t),o=r.substring(n);return{start:t,end:n,value:r,beforeTxt:i,afterTxt:o}}catch{}}function
$S(e,t){if(!(!e||e.ownerDocument.activeElement!==e)){if(!t){e.setSelectionRange(e.value.length,e.value.length);return}try{const{value:n}=e,{beforeTxt:r="",afterTxt:i="",start:o}=t;let
s=n.length;if(n.endsWith(i))s=n.length-i.length;else [...]
+ </svg>`,n.body.appendChild(r)};function
pE(e){if(!(!e||e.ownerDocument.activeElement!==e))try{const{selectionStart:t,selectionEnd:n,value:r}=e,i=r.substring(0,t),o=r.substring(n);return{start:t,end:n,value:r,beforeTxt:i,afterTxt:o}}catch{}}function
mE(e,t){if(!(!e||e.ownerDocument.activeElement!==e)){if(!t){e.setSelectionRange(e.value.length,e.value.length);return}try{const{value:n}=e,{beforeTxt:r="",afterTxt:i="",start:o}=t;let
s=n.length;if(n.endsWith(i))s=n.length-i.length;else [...]
)+\\(\\s*max(-device)?-${e}`,"i"),min:new
RegExp(`\\(\\s*min(-device)?-${e}`,"i"),maxMin:new
RegExp(`(!?\\(\\s*max(-device)?-${e})(.|
-)+\\(\\s*min(-device)?-${e}`,"i"),max:new
RegExp(`\\(\\s*max(-device)?-${e}`,"i")}),Dw=mh("width"),Mw=mh("height"),vh=e=>({isMin:wh(e.minMax,e.maxMin,e.min),isMax:wh(e.maxMin,e.minMax,e.max)}),{isMin:Ga,isMax:bh}=vh(Dw),{isMin:qa,isMax:yh}=vh(Mw),xh=/print/i,Ch=/^print$/i,$w=/(-?\d*\.?\d+)(ch|em|ex|px|rem)/,Bw=/(\d)/,di=Number.MAX_VALUE,jw={ch:8.8984375,em:16,rem:16,ex:8.296875,px:1};function
Sh(e){const t=$w.exec(e)||(Ga(e)||qa(e)?Bw.exec(e):null);if(!t)return
di;if(t[0]==="0")return 0; [...]
-`).forEach(function(s){i=s.indexOf(":"),n=s.substring(0,i).trim().toLowerCase(),r=s.substring(i+1).trim(),!(!n||t[n]&&e2[n])&&(n==="set-cookie"?t[n]?t[n].push(r):t[n]=[r]:t[n]=t[n]?t[n]+",
"+r:r)}),t},lg=Symbol("internals");function yi(e){return
e&&String(e).trim().toLowerCase()}function Yo(e){return
e===!1||e==null?e:k.isArray(e)?e.map(Yo):String(e)}function n2(e){const
t=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let
r;for(;r=n.exec(e);)t[r[1]]=r[2];return t}const r2=e=>/ [...]
-`)}getSetCookie(){return
this.get("set-cookie")||[]}get[Symbol.toStringTag](){return"AxiosHeaders"}static
from(t){return t instanceof this?t:new this(t)}static concat(t,...n){const
r=new this(t);return n.forEach(i=>r.set(i)),r}static accessor(t){const
r=(this[lg]=this[lg]={accessors:{}}).accessors,i=this.prototype;function
o(s){const a=yi(s);r[a]||(o2(i,s),r[a]=!0)}return
k.isArray(t)?t.forEach(o):o(t),this}};Ue.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-A
[...]
-`+o.map(xg).join(`
-`):" "+xg(o[0]):"as no adapter specified";throw new W("There is no suitable
adapter to dispatch the request "+s,"ERR_NOT_SUPPORT")}return
r},adapters:xl};function
Cl(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw
new Cr(null,e)}function Sg(e){return
Cl(e),e.headers=Ue.from(e.headers),e.data=bl.call(e,e.transformRequest),["post","put","patch"].indexOf(e.method)!==-1&&e.headers.setContentType("application/x-www-form-urlencoded",!1),Cg.getAdapter(e.ad
[...]
-`+o):r.stack=o}catch{}}throw r}}_request(t,n){typeof
t=="string"?(n=n||{},n.url=t):n=t||{},n=zn(this.defaults,n);const{transitional:r,paramsSerializer:i,headers:o}=n;r!==void
0&&ts.assertOptions(r,{silentJSONParsing:Pt.transitional(Pt.boolean),forcedJSONParsing:Pt.transitional(Pt.boolean),clarifyTimeoutError:Pt.transitional(Pt.boolean)},!1),i!=null&&(k.isFunction(i)?n.paramsSerializer={serialize:i}:ts.assertOptions(i,{encode:Pt.function,serialize:Pt.function},!0)),n.allowAbsoluteUrls!==v
[...]
+)+\\(\\s*min(-device)?-${e}`,"i"),max:new
RegExp(`\\(\\s*max(-device)?-${e}`,"i")}),Sk=Cf("width"),wk=Cf("height"),Sf=e=>({isMin:Pf(e.minMax,e.maxMin,e.min),isMax:Pf(e.maxMin,e.minMax,e.max)}),{isMin:kl,isMax:wf}=Sf(Sk),{isMin:Ol,isMax:Ef}=Sf(wk),kf=/print/i,Of=/^print$/i,Ek=/(-?\d*\.?\d+)(ch|em|ex|px|rem)/,kk=/(\d)/,Oi=Number.MAX_VALUE,Ok={ch:8.8984375,em:16,rem:16,ex:8.296875,px:1};function
If(e){const t=Ek.exec(e)||(kl(e)||Ol(e)?kk.exec(e):null);if(!t)return
Oi;if(t[0]==="0")return 0; [...]
+`).forEach(function(s){i=s.indexOf(":"),n=s.substring(0,i).trim().toLowerCase(),r=s.substring(i+1).trim(),!(!n||t[n]&&QT[n])&&(n==="set-cookie"?t[n]?t[n].push(r):t[n]=[r]:t[n]=t[n]?t[n]+",
"+r:r)}),t},up=Symbol("internals");function _i(e){return
e&&String(e).trim().toLowerCase()}function Cs(e){return
e===!1||e==null?e:k.isArray(e)?e.map(Cs):String(e)}function ZT(e){const
t=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let
r;for(;r=n.exec(e);)t[r[1]]=r[2];return t}const e2=e=>/ [...]
+`)}getSetCookie(){return
this.get("set-cookie")||[]}get[Symbol.toStringTag](){return"AxiosHeaders"}static
from(t){return t instanceof this?t:new this(t)}static concat(t,...n){const
r=new this(t);return n.forEach(i=>r.set(i)),r}static accessor(t){const
r=(this[up]=this[up]={accessors:{}}).accessors,i=this.prototype;function
o(s){const a=_i(s);r[a]||(n2(i,s),r[a]=!0)}return
k.isArray(t)?t.forEach(o):o(t),this}};Xe.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-A
[...]
+`+o.map(Sp).join(`
+`):" "+Sp(o[0]):"as no adapter specified";throw new Q("There is no suitable
adapter to dispatch the request "+s,"ERR_NOT_SUPPORT")}return
r},adapters:tc};function
nc(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw
new Vr(null,e)}function Ep(e){return
nc(e),e.headers=Xe.from(e.headers),e.data=Zl.call(e,e.transformRequest),["post","put","patch"].indexOf(e.method)!==-1&&e.headers.setContentType("application/x-www-form-urlencoded",!1),wp.getAdapter(e.ad
[...]
+`+o):r.stack=o}catch{}}throw r}}_request(t,n){typeof
t=="string"?(n=n||{},n.url=t):n=t||{},n=qn(this.defaults,n);const{transitional:r,paramsSerializer:i,headers:o}=n;r!==void
0&&Os.assertOptions(r,{silentJSONParsing:Vt.transitional(Vt.boolean),forcedJSONParsing:Vt.transitional(Vt.boolean),clarifyTimeoutError:Vt.transitional(Vt.boolean)},!1),i!=null&&(k.isFunction(i)?n.paramsSerializer={serialize:i}:Os.assertOptions(i,{encode:Vt.function,serialize:Vt.function},!0)),n.allowAbsoluteUrls!==v
[...]
* @remix-run/router v1.23.0
*
* Copyright (c) Remix Software Inc.
@@ -35,7 +35,7 @@
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
- */function xi(){return
xi=Object.assign?Object.assign.bind():function(e){for(var
t=1;t<arguments.length;t++){var n=arguments[t];for(var r in
n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return
e},xi.apply(this,arguments)}var
tn;(function(e){e.Pop="POP",e.Push="PUSH",e.Replace="REPLACE"})(tn||(tn={}));const
Pg="popstate";function V2(e){e===void 0&&(e={});function
t(r,i){let{pathname:o,search:s,hash:a}=r.location;return
kl("",{pathname:o,search:s,hash:a},i.state&&i.state.usr|| [...]
+ */function Vi(){return
Vi=Object.assign?Object.assign.bind():function(e){for(var
t=1;t<arguments.length;t++){var n=arguments[t];for(var r in
n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return
e},Vi.apply(this,arguments)}var
cn;(function(e){e.Pop="POP",e.Push="PUSH",e.Replace="REPLACE"})(cn||(cn={}));const
Np="popstate";function N2(e){e===void 0&&(e={});function
t(r,i){let{pathname:o,search:s,hash:a}=r.location;return
oc("",{pathname:o,search:s,hash:a},i.state&&i.state.usr|| [...]
* React Router v6.30.1
*
* Copyright (c) Remix Software Inc.
@@ -44,7 +44,7 @@
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
- */function Ci(){return
Ci=Object.assign?Object.assign.bind():function(e){for(var
t=1;t<arguments.length;t++){var n=arguments[t];for(var r in
n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return
e},Ci.apply(this,arguments)}const
is=w.createContext(null),Mg=w.createContext(null),rn=w.createContext(null),os=w.createContext(null),Mn=w.createContext({outlet:null,matches:[],isDataRoute:!1}),$g=w.createContext(null);function
oP(e,t){let{relative:n}=t===void 0?{}:t;Si()||de(!1);let{b [...]
+ */function Fi(){return
Fi=Object.assign?Object.assign.bind():function(e){for(var
t=1;t<arguments.length;t++){var n=arguments[t];for(var r in
n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return
e},Fi.apply(this,arguments)}const
Rs=O.createContext(null),Bp=O.createContext(null),dn=O.createContext(null),Ts=O.createContext(null),Xn=O.createContext({outlet:null,matches:[],isDataRoute:!1}),jp=O.createContext(null);function
nN(e,t){let{relative:n}=t===void 0?{}:t;Li()||pe(!1);let{b [...]
* React Router DOM v6.30.1
*
* Copyright (c) Remix Software Inc.
@@ -53,7 +53,7 @@
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
- */function as(){return
as=Object.assign?Object.assign.bind():function(e){for(var
t=1;t<arguments.length;t++){var n=arguments[t];for(var r in
n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return
e},as.apply(this,arguments)}function Gg(e,t){if(e==null)return{};var
n={},r=Object.keys(e),i,o;for(o=0;o<r.length;o++)i=r[o],!(t.indexOf(i)>=0)&&(n[i]=e[i]);return
n}function kP(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function
EP(e,t){return e.button===0&&(!t||t==="_sel [...]
+ */function As(){return
As=Object.assign?Object.assign.bind():function(e){for(var
t=1;t<arguments.length;t++){var n=arguments[t];for(var r in
n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return
e},As.apply(this,arguments)}function Kp(e,t){if(e==null)return{};var
n={},r=Object.keys(e),i,o;for(o=0;o<r.length;o++)i=r[o],!(t.indexOf(i)>=0)&&(n[i]=e[i]);return
n}function CN(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function
SN(e,t){return e.button===0&&(!t||t==="_sel [...]
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -70,7 +70,7 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
- */const ap=5e3;/*!
+ */const
mA=zE({pauseOnPageIdle:!0,placement:"bottom-end"}),sm=({error:e})=>{var i;const
t=e;if(!t)return;const n=(i=t.body)==null?void 0:i.detail;let r;return n!==void
0&&(typeof n=="string"?r=n:Array.isArray(n)?r=n.map(o=>`${o.loc.join(".")}
${o.msg}`):r=Object.keys(n).map(o=>`${o}:
${n[o]}`)),y.jsx(pA,{status:"error",children:y.jsxs(fR,{align:"start",flexDirection:"column",gap:2,mt:-1,children:[t.status,"
",t.message,r===t.message?void
0:y.jsx(Wx,{whiteSpace:"preserve",wordBreak:"brea [...]
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -87,7 +87,7 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
- */const RT=e=>{const[t,n]=T.useState(0);return
T.useEffect(()=>{if(!e.current)return;const r=new ResizeObserver(i=>{for(const
o of i)n(o.contentRect.width)});return
r.observe(e.current),()=>{r.disconnect()}},[e]),t};/*!
+ */const cm=5e3;/*!
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -104,7 +104,7 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
- */const lp="token",PT=()=>{const e=document.cookie.split(";");for(const t of
e){const[n,r]=t.split("=");if((n==null?void 0:n.trim())==="_token"&&r!==void
0)return localStorage.setItem(lp,r),document.cookie="_token=; expires=Sat, 01
Jan 2000 00:00:00 UTC; path=/;",r}},TT=e=>{const
t=localStorage.getItem(lp)??PT();return t!==void
0&&(e.headers.Authorization=`Bearer
${t}`),e},NT=()=>{const{data:e,error:t}=nT(void
0,{enabled:!0,refetchInterval:ap});return e?S.jsx(At,{p:2,children:S.jsxs(mf,
[...]
+ */const OA=e=>{const[t,n]=E.useState(0);return
E.useEffect(()=>{if(!e.current)return;const r=new ResizeObserver(i=>{for(const
o of i)n(o.contentRect.width)});return
r.observe(e.current),()=>{r.disconnect()}},[e]),t};/*!
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -121,4 +121,21 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
- */const
on=(e,t="white")=>({solid:{value:`{colors.${e}.600}`},contrast:{value:{_light:"white",_dark:t}},fg:{value:{_light:`{colors.${e}.800}`,_dark:`{colors.${e}.200}`}},muted:{value:{_light:`{colors.${e}.200}`,_dark:`{colors.${e}.800}`}},subtle:{value:{_light:`{colors.${e}.100}`,_dark:`{colors.${e}.900}`}},emphasized:{value:{_light:`{colors.${e}.300}`,_dark:`{colors.${e}.700}`}},focusRing:{value:{_light:`{colors.${e}.800}`,_dark:`{colors.${e}.200}`}}}),qT=ja({theme:{tokens:{colors:{suc
[...]
+ */const um="token",IA=()=>{const e=document.cookie.split(";");for(const t of
e){const[n,r]=t.split("=");if((n==null?void 0:n.trim())==="_token"&&r!==void
0)return localStorage.setItem(um,r),document.cookie="_token=; expires=Sat, 01
Jan 2000 00:00:00 UTC; path=/;",r}},PA=e=>{const
t=localStorage.getItem(um)??IA();return t!==void
0&&(e.headers.Authorization=`Bearer
${t}`),e},RA=()=>{const{data:e,error:t}=ZN(void
0,{enabled:!0,refetchInterval:cm});return e?y.jsx(zt,{p:2,children:y.jsxs(bg,
[...]
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */const
hn=(e,t="white")=>({solid:{value:`{colors.${e}.600}`},contrast:{value:{_light:"white",_dark:t}},fg:{value:{_light:`{colors.${e}.800}`,_dark:`{colors.${e}.200}`}},muted:{value:{_light:`{colors.${e}.200}`,_dark:`{colors.${e}.800}`}},subtle:{value:{_light:`{colors.${e}.100}`,_dark:`{colors.${e}.900}`}},emphasized:{value:{_light:`{colors.${e}.300}`,_dark:`{colors.${e}.700}`}},focusRing:{value:{_light:`{colors.${e}.800}`,_dark:`{colors.${e}.200}`}}}),HA=Cl({theme:{tokens:{colors:{suc
[...]
diff --git
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/MaintenanceEnterButton.tsx
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/MaintenanceEnterButton.tsx
new file mode 100644
index 00000000000..a7d526c0095
--- /dev/null
+++
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/MaintenanceEnterButton.tsx
@@ -0,0 +1,101 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Button, CloseButton, Dialog, IconButton, Portal, Textarea,
useDisclosure } from "@chakra-ui/react";
+import { useUiServiceRequestWorkerMaintenance } from "openapi/queries";
+import { useState } from "react";
+import { HiOutlineWrenchScrewdriver } from "react-icons/hi2";
+
+interface MaintenanceEnterButtonProps {
+ onEnterMaintenance: (toast: Record<string, string>) => void;
+ workerName: string;
+}
+
+export const MaintenanceEnterButton = ({ onEnterMaintenance, workerName }:
MaintenanceEnterButtonProps) => {
+ const { onClose, onOpen, open } = useDisclosure();
+ const [comment, setComment] = useState("");
+
+ const enterMaintenanceMutation = useUiServiceRequestWorkerMaintenance({
+ onError: (error) => {
+ onEnterMaintenance({
+ description: `Unable to set worker ${workerName} to maintenance mode:
${error}`,
+ title: "Setting Maintenance Mode failed",
+ type: "error",
+ });
+ },
+ onSuccess: () => {
+ onEnterMaintenance({
+ description: `Worker ${workerName} was requested to be in maintenance
mode.`,
+ title: "Maintenance Mode activated",
+ type: "success",
+ });
+ },
+ });
+
+ const enterMaintenance = () => {
+ enterMaintenanceMutation.mutate({ requestBody: { maintenance_comment:
comment }, workerName });
+ };
+
+ return (
+ <>
+ <IconButton
+ size="sm"
+ variant="ghost"
+ aria-label="Enter Maintenance"
+ title="Enter Maintenance"
+ onClick={onOpen}
+ >
+ <HiOutlineWrenchScrewdriver />
+ </IconButton>
+
+ <Dialog.Root onOpenChange={onClose} open={open} size="md">
+ <Portal>
+ <Dialog.Backdrop />
+ <Dialog.Positioner>
+ <Dialog.Content>
+ <Dialog.Header>
+ <Dialog.Title>Set maintenance for worker
{workerName}</Dialog.Title>
+ </Dialog.Header>
+ <Dialog.Body>
+ <Textarea
+ placeholder="Enter maintenance comment (required)"
+ value={comment}
+ onChange={(e) => setComment(e.target.value)}
+ required
+ maxLength={1024}
+ size="sm"
+ />
+ </Dialog.Body>
+ <Dialog.Footer>
+ <Dialog.ActionTrigger asChild>
+ <Button variant="outline">Cancel</Button>
+ </Dialog.ActionTrigger>
+ <Button onClick={enterMaintenance} disabled={!comment.trim()}>
+ Confirm Maintenance
+ </Button>
+ </Dialog.Footer>
+ <Dialog.CloseTrigger asChild>
+ <CloseButton size="sm" />
+ </Dialog.CloseTrigger>
+ </Dialog.Content>
+ </Dialog.Positioner>
+ </Portal>
+ </Dialog.Root>
+ </>
+ );
+};
diff --git
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/MaintenanceExitButton.tsx
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/MaintenanceExitButton.tsx
new file mode 100644
index 00000000000..7ebb04c1572
--- /dev/null
+++
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/MaintenanceExitButton.tsx
@@ -0,0 +1,61 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { IconButton } from "@chakra-ui/react";
+import { useUiServiceExitWorkerMaintenance } from "openapi/queries";
+import { IoMdExit } from "react-icons/io";
+
+interface MaintenanceExitButtonProps {
+ onExitMaintenance: (toast: Record<string, string>) => void;
+ workerName: string;
+}
+
+export const MaintenanceExitButton = ({ onExitMaintenance, workerName }:
MaintenanceExitButtonProps) => {
+ const exitMaintenanceMutation = useUiServiceExitWorkerMaintenance({
+ onError: (error) => {
+ onExitMaintenance({
+ description: `Unable to exit ${workerName} from maintenance mode:
${error}`,
+ title: "Exit Maintenance Mode failed",
+ type: "error",
+ });
+ },
+ onSuccess: () => {
+ onExitMaintenance({
+ description: `Worker ${workerName} was requested to exit maintenance
mode.`,
+ title: "Maintenance Mode deactivated",
+ type: "success",
+ });
+ },
+ });
+
+ const exitMaintenance = () => {
+ exitMaintenanceMutation.mutate({ workerName });
+ };
+
+ return (
+ <IconButton
+ size="sm"
+ variant="ghost"
+ onClick={() => exitMaintenance()}
+ aria-label="Exit Maintenance"
+ title="Exit Maintenance"
+ >
+ <IoMdExit />
+ </IconButton>
+ );
+};
diff --git
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/OperationsCell.tsx
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/OperationsCell.tsx
deleted file mode 100644
index bf7fe3b4d94..00000000000
---
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/OperationsCell.tsx
+++ /dev/null
@@ -1,147 +0,0 @@
-/*!
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import { Box, Flex, HStack, IconButton, Textarea, VStack } from
"@chakra-ui/react";
-import type { Worker } from "openapi/requests/types.gen";
-import { useState } from "react";
-import { FcCheckmark } from "react-icons/fc";
-import { HiOutlineWrenchScrewdriver } from "react-icons/hi2";
-import { ImCross } from "react-icons/im";
-import { IoMdExit } from "react-icons/io";
-
-interface MaintenanceFormProps {
- onSubmit: (comment: string) => void;
- onCancel: () => void;
-}
-
-const MaintenanceForm = ({ onCancel, onSubmit }: MaintenanceFormProps) => {
- const [comment, setComment] = useState("");
-
- const handleSubmit = () => {
- if (comment.trim()) {
- onSubmit(comment.trim());
- }
- };
-
- return (
- <VStack gap={2} align="stretch">
- <Textarea
- placeholder="Enter maintenance comment (required)"
- value={comment}
- onChange={(e) => setComment(e.target.value)}
- required
- maxLength={1024}
- size="sm"
- />
- <HStack gap={2}>
- <IconButton
- size="sm"
- colorScheme="green"
- onClick={handleSubmit}
- disabled={!comment.trim()}
- aria-label="Confirm Maintenance"
- title="Confirm Maintenance"
- >
- <FcCheckmark />
- </IconButton>
- <IconButton
- size="sm"
- colorScheme="red"
- variant="outline"
- onClick={onCancel}
- aria-label="Cancel"
- title="Cancel"
- >
- <ImCross />
- </IconButton>
- </HStack>
- </VStack>
- );
-};
-
-interface OperationsCellProps {
- worker: Worker;
- activeMaintenanceForm: string | null;
- onSetActiveMaintenanceForm: (workerName: string | null) => void;
- onRequestMaintenance: (workerName: string, comment: string) => void;
- onExitMaintenance: (workerName: string) => void;
-}
-
-export const OperationsCell = ({
- activeMaintenanceForm,
- onExitMaintenance,
- onRequestMaintenance,
- onSetActiveMaintenanceForm,
- worker,
-}: OperationsCellProps) => {
- const workerName = worker.worker_name;
- const state = worker.state;
-
- let cellContent = null;
-
- if (state === "idle" || state === "running") {
- if (activeMaintenanceForm === workerName) {
- cellContent = (
- <MaintenanceForm
- onSubmit={(comment) => onRequestMaintenance(workerName, comment)}
- onCancel={() => onSetActiveMaintenanceForm(null)}
- />
- );
- } else {
- cellContent = (
- <Flex justifyContent="end">
- <IconButton
- size="sm"
- variant="ghost"
- onClick={() => onSetActiveMaintenanceForm(workerName)}
- aria-label="Enter Maintenance"
- title="Enter Maintenance"
- >
- <HiOutlineWrenchScrewdriver />
- </IconButton>
- </Flex>
- );
- }
- } else if (
- state === "maintenance pending" ||
- state === "maintenance mode" ||
- state === "maintenance request" ||
- state === "offline maintenance"
- ) {
- cellContent = (
- <VStack gap={2} align="stretch">
- <Box fontSize="sm" whiteSpace="pre-wrap">
- {worker.maintenance_comments || "No comment"}
- </Box>
- <Flex justifyContent="end">
- <IconButton
- size="sm"
- variant="ghost"
- onClick={() => onExitMaintenance(workerName)}
- aria-label="Exit Maintenance"
- title="Exit Maintenance"
- >
- <IoMdExit />
- </IconButton>
- </Flex>
- </VStack>
- );
- }
-
- return cellContent;
-};
diff --git
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/WorkerOperations.tsx
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/WorkerOperations.tsx
new file mode 100644
index 00000000000..34ce5fd6676
--- /dev/null
+++
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/WorkerOperations.tsx
@@ -0,0 +1,65 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Box, Flex, VStack } from "@chakra-ui/react";
+import type { Worker } from "openapi/requests/types.gen";
+
+import { toaster } from "src/components/ui";
+
+import { MaintenanceEnterButton } from "./MaintenanceEnterButton";
+import { MaintenanceExitButton } from "./MaintenanceExitButton";
+
+interface WorkerOperationsProps {
+ onOperations: () => void;
+ worker: Worker;
+}
+
+export const WorkerOperations = ({ onOperations, worker }:
WorkerOperationsProps) => {
+ const workerName = worker.worker_name;
+ const state = worker.state;
+
+ const onWorkerChange = (toast: Record<string, string>) => {
+ toaster.create(toast);
+ onOperations();
+ };
+
+ if (state === "idle" || state === "running") {
+ return (
+ <Flex justifyContent="end">
+ <MaintenanceEnterButton onEnterMaintenance={onWorkerChange}
workerName={workerName} />
+ </Flex>
+ );
+ } else if (
+ state === "maintenance pending" ||
+ state === "maintenance mode" ||
+ state === "maintenance request" ||
+ state === "offline maintenance"
+ ) {
+ return (
+ <VStack gap={2} align="stretch">
+ <Box fontSize="sm" whiteSpace="pre-wrap">
+ {worker.maintenance_comments || "No comment"}
+ </Box>
+ <Flex justifyContent="end">
+ <MaintenanceExitButton onExitMaintenance={onWorkerChange}
workerName={workerName} />
+ </Flex>
+ </VStack>
+ );
+ }
+ return null;
+};
diff --git
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/index.ts
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/createToaster.ts
similarity index 84%
copy from
providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/index.ts
copy to
providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/createToaster.ts
index 62ff4eda180..9fa1c393c77 100644
---
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/index.ts
+++
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/createToaster.ts
@@ -16,5 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { createToaster } from "@chakra-ui/react";
-export * from "./Alert";
+export const toaster = createToaster({
+ pauseOnPageIdle: true,
+ placement: "bottom-end",
+});
diff --git
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/index.ts
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/index.ts
index 62ff4eda180..95ef0889d86 100644
---
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/index.ts
+++
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/index.ts
@@ -18,3 +18,4 @@
*/
export * from "./Alert";
+export * from "./createToaster";
diff --git
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/pages/WorkerPage.tsx
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/pages/WorkerPage.tsx
index ff6a1745532..0351b2d608e 100644
---
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/pages/WorkerPage.tsx
+++
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/pages/WorkerPage.tsx
@@ -17,15 +17,10 @@
* under the License.
*/
import { Box, Table } from "@chakra-ui/react";
-import {
- useUiServiceWorker,
- useUiServiceRequestWorkerMaintenance,
- useUiServiceExitWorkerMaintenance,
-} from "openapi/queries";
-import { useState } from "react";
+import { useUiServiceWorker } from "openapi/queries";
import { ErrorAlert } from "src/components/ErrorAlert";
-import { OperationsCell } from "src/components/OperationsCell";
+import { WorkerOperations } from "src/components/WorkerOperations";
import { WorkerStateBadge } from "src/components/WorkerStateBadge";
import { autoRefreshInterval } from "src/utils";
@@ -34,43 +29,6 @@ export const WorkerPage = () => {
enabled: true,
refetchInterval: autoRefreshInterval,
});
- const [activeMaintenanceForm, setActiveMaintenanceForm] = useState<string |
null>(null);
-
- const requestMaintenanceMutation = useUiServiceRequestWorkerMaintenance({
- onError: (error) => {
- console.error("Error requesting maintenance:", error);
- alert(`Error requesting maintenance: ${error}`);
- },
- onSuccess: () => {
- console.log("Maintenance request successful");
- setActiveMaintenanceForm(null);
- refetch();
- },
- });
-
- const exitMaintenanceMutation = useUiServiceExitWorkerMaintenance({
- onError: (error) => {
- console.error("Error exiting maintenance:", error);
- alert(`Error exiting maintenance: ${error}`);
- },
- onSuccess: () => {
- console.log("Exit maintenance successful");
- refetch();
- },
- });
-
- const requestMaintenance = (workerName: string, comment: string) => {
- console.log(`Requesting maintenance for worker: ${workerName}, comment:
${comment}`);
- requestMaintenanceMutation.mutate({
- requestBody: { maintenance_comment: comment },
- workerName,
- });
- };
-
- const exitMaintenance = (workerName: string) => {
- console.log(`Exiting maintenance for worker: ${workerName}`);
- exitMaintenanceMutation.mutate({ workerName });
- };
// TODO to make it proper
// Use DataTable as component from Airflow-Core UI
@@ -129,13 +87,7 @@ export const WorkerPage = () => {
)}
</Table.Cell>
<Table.Cell>
- <OperationsCell
- worker={worker}
- activeMaintenanceForm={activeMaintenanceForm}
- onSetActiveMaintenanceForm={setActiveMaintenanceForm}
- onRequestMaintenance={requestMaintenance}
- onExitMaintenance={exitMaintenance}
- />
+ <WorkerOperations worker={worker} onOperations={refetch} />
</Table.Cell>
</Table.Row>
))}
diff --git
a/providers/edge3/src/airflow/providers/edge3/plugins/www/vite.config.ts
b/providers/edge3/src/airflow/providers/edge3/plugins/www/vite.config.ts
index e364349872f..37ebf1eca9e 100644
--- a/providers/edge3/src/airflow/providers/edge3/plugins/www/vite.config.ts
+++ b/providers/edge3/src/airflow/providers/edge3/plugins/www/vite.config.ts
@@ -42,6 +42,7 @@ export default defineConfig(({ command }) => {
output: {
globals: {
react: "React",
+ "react-dom": "ReactDOM",
"react/jsx-runtime": "ReactJSXRuntime",
},
},
diff --git
a/providers/edge3/src/airflow/providers/edge3/worker_api/routes/ui.py
b/providers/edge3/src/airflow/providers/edge3/worker_api/routes/ui.py
index 11027310768..0b452691765 100644
--- a/providers/edge3/src/airflow/providers/edge3/worker_api/routes/ui.py
+++ b/providers/edge3/src/airflow/providers/edge3/worker_api/routes/ui.py
@@ -19,7 +19,7 @@ from __future__ import annotations
from datetime import datetime
-from fastapi import Depends, HTTPException
+from fastapi import Depends, HTTPException, status
from sqlalchemy import select
from airflow.api_fastapi.auth.managers.models.resource_details import
AccessView
@@ -122,7 +122,9 @@ def request_worker_maintenance(
worker_query = select(EdgeWorkerModel).where(EdgeWorkerModel.worker_name
== worker_name)
worker = session.scalar(worker_query)
if not worker:
- raise HTTPException(status_code=404, detail=f"Worker {worker_name} not
found")
+ raise HTTPException(status.HTTP_404_NOT_FOUND, detail=f"Worker
{worker_name} not found")
+ if not maintenance_request.maintenance_comment:
+ raise HTTPException(status.HTTP_400_BAD_REQUEST, detail="Maintenance
comment is required")
# Format the comment with timestamp and username (username will be added
by plugin layer)
formatted_comment = f"[{datetime.now().strftime('%Y-%m-%d %H:%M')}] -
{user.get_name()} put node into maintenance mode\nComment:
{maintenance_request.maintenance_comment}"
@@ -130,7 +132,7 @@ def request_worker_maintenance(
try:
request_maintenance(worker_name, formatted_comment, session=session)
except Exception as e:
- raise HTTPException(status_code=400, detail=str(e))
+ raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=str(e))
@ui_router.delete(
@@ -148,9 +150,9 @@ def exit_worker_maintenance(
worker_query = select(EdgeWorkerModel).where(EdgeWorkerModel.worker_name
== worker_name)
worker = session.scalar(worker_query)
if not worker:
- raise HTTPException(status_code=404, detail=f"Worker {worker_name} not
found")
+ raise HTTPException(status.HTTP_404_NOT_FOUND, detail=f"Worker
{worker_name} not found")
try:
exit_maintenance(worker_name, session=session)
except Exception as e:
- raise HTTPException(status_code=400, detail=str(e))
+ raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=str(e))
diff --git a/providers/edge3/www-hash.txt b/providers/edge3/www-hash.txt
index 0e053e9262d..e77d9fccb14 100644
--- a/providers/edge3/www-hash.txt
+++ b/providers/edge3/www-hash.txt
@@ -1 +1 @@
-ed85f7d6558cdc8c5edf498fcd96e187484327b877e021314f94ae640d4634f2
+d3d458dbc15ae801bb6bf8f128e38a1a65fd81e3ecc8c87dd30a79acc9a4041a