apache-airflow-providers-fab 1.5.3__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. airflow/providers/fab/LICENSE +0 -52
  2. airflow/providers/fab/__init__.py +3 -3
  3. airflow/providers/fab/auth_manager/api/auth/backend/basic_auth.py +4 -5
  4. airflow/providers/fab/auth_manager/api/auth/backend/kerberos_auth.py +5 -5
  5. airflow/providers/fab/auth_manager/api/auth/backend/session.py +2 -2
  6. airflow/providers/fab/auth_manager/api_endpoints/role_and_permission_endpoint.py +15 -15
  7. airflow/providers/fab/auth_manager/api_endpoints/user_endpoint.py +13 -14
  8. airflow/providers/fab/auth_manager/api_fastapi/__init__.py +16 -0
  9. airflow/providers/fab/auth_manager/api_fastapi/datamodels/__init__.py +16 -0
  10. airflow/providers/fab/auth_manager/api_fastapi/datamodels/login.py +32 -0
  11. airflow/providers/fab/auth_manager/api_fastapi/openapi/__init__.py +16 -0
  12. airflow/providers/fab/auth_manager/api_fastapi/openapi/v1-generated.yaml +153 -0
  13. airflow/providers/fab/auth_manager/api_fastapi/routes/__init__.py +16 -0
  14. airflow/providers/fab/auth_manager/api_fastapi/routes/login.py +51 -0
  15. airflow/providers/fab/auth_manager/api_fastapi/services/__init__.py +16 -0
  16. airflow/providers/fab/auth_manager/api_fastapi/services/login.py +58 -0
  17. airflow/providers/fab/auth_manager/cli_commands/db_command.py +1 -3
  18. airflow/providers/fab/auth_manager/cli_commands/user_command.py +2 -2
  19. airflow/providers/fab/auth_manager/cli_commands/utils.py +12 -11
  20. airflow/providers/fab/auth_manager/fab_auth_manager.py +238 -126
  21. airflow/providers/fab/auth_manager/models/__init__.py +1 -1
  22. airflow/providers/fab/auth_manager/models/anonymous_user.py +1 -1
  23. airflow/providers/fab/auth_manager/models/db.py +22 -5
  24. airflow/providers/fab/auth_manager/openapi/v1.yaml +9 -0
  25. airflow/providers/fab/auth_manager/schemas/user_schema.py +1 -1
  26. airflow/providers/fab/auth_manager/security_manager/override.py +186 -655
  27. airflow/providers/fab/auth_manager/views/permissions.py +1 -1
  28. airflow/providers/fab/auth_manager/views/roles_list.py +1 -1
  29. airflow/providers/fab/auth_manager/views/user.py +1 -1
  30. airflow/providers/fab/auth_manager/views/user_edit.py +1 -1
  31. airflow/providers/fab/auth_manager/views/user_stats.py +1 -1
  32. airflow/providers/fab/get_provider_info.py +29 -34
  33. airflow/providers/fab/www/airflow_flask_app.py +31 -0
  34. airflow/providers/fab/www/api_connexion/__init__.py +17 -0
  35. airflow/providers/fab/www/api_connexion/exceptions.py +197 -0
  36. airflow/providers/fab/www/api_connexion/parameters.py +131 -0
  37. airflow/providers/fab/www/api_connexion/security.py +84 -0
  38. airflow/providers/fab/www/api_connexion/types.py +30 -0
  39. airflow/providers/fab/www/app.py +120 -0
  40. airflow/providers/fab/www/auth.py +350 -0
  41. airflow/providers/fab/www/constants.py +28 -0
  42. airflow/providers/fab/www/extensions/__init__.py +16 -0
  43. airflow/providers/fab/www/extensions/init_appbuilder.py +606 -0
  44. airflow/providers/fab/www/extensions/init_jinja_globals.py +82 -0
  45. airflow/providers/fab/www/extensions/init_manifest_files.py +61 -0
  46. airflow/providers/fab/www/extensions/init_security.py +61 -0
  47. airflow/providers/fab/www/extensions/init_session.py +64 -0
  48. airflow/providers/fab/www/extensions/init_views.py +177 -0
  49. airflow/providers/fab/www/package-lock.json +8939 -0
  50. airflow/providers/fab/www/package.json +77 -0
  51. airflow/providers/fab/www/security/__init__.py +17 -0
  52. airflow/providers/fab/www/security/permissions.py +126 -0
  53. airflow/providers/fab/www/security_appless.py +44 -0
  54. airflow/providers/fab/www/security_manager.py +122 -0
  55. airflow/providers/fab/www/session.py +41 -0
  56. airflow/providers/fab/www/static/css/bootstrap-theme.css +6215 -0
  57. airflow/providers/fab/www/static/css/flash.css +57 -0
  58. airflow/providers/fab/www/static/css/loading-dots.css +60 -0
  59. airflow/providers/fab/www/static/css/main.css +676 -0
  60. airflow/providers/fab/www/static/css/material-icons.css +84 -0
  61. airflow/providers/fab/www/static/dist/48f0ea180c40270a5b05.png +1 -0
  62. airflow/providers/fab/www/static/dist/649c0b07771e68fafdeb.png +1 -0
  63. airflow/providers/fab/www/static/dist/airflowDefaultTheme.feec4a4075c2f3d6ae01.css +33 -0
  64. airflow/providers/fab/www/static/dist/airflowDefaultTheme.feec4a4075c2f3d6ae01.js +1 -0
  65. airflow/providers/fab/www/static/dist/f7490d556a6c42e49ba4.png +1 -0
  66. airflow/providers/fab/www/static/dist/flash.137b30cff85b5588e661.css +18 -0
  67. airflow/providers/fab/www/static/dist/flash.137b30cff85b5588e661.js +1 -0
  68. airflow/providers/fab/www/static/dist/jquery-ui.min.css +5 -0
  69. airflow/providers/fab/www/static/dist/jquery-ui.min.js +2 -0
  70. airflow/providers/fab/www/static/dist/jquery-ui.min.js.LICENSE.txt +4 -0
  71. airflow/providers/fab/www/static/dist/loadingDots.48ab7d5b04e66f2686b0.css +18 -0
  72. airflow/providers/fab/www/static/dist/loadingDots.48ab7d5b04e66f2686b0.js +1 -0
  73. airflow/providers/fab/www/static/dist/main.edb2d40dfbbc537916e3.css +18 -0
  74. airflow/providers/fab/www/static/dist/main.edb2d40dfbbc537916e3.js +2 -0
  75. airflow/providers/fab/www/static/dist/main.edb2d40dfbbc537916e3.js.LICENSE.txt +18 -0
  76. airflow/providers/fab/www/static/dist/manifest.json +20 -0
  77. airflow/providers/fab/www/static/dist/materialIcons.57390fa60d8f61175334.css +18 -0
  78. airflow/providers/fab/www/static/dist/materialIcons.57390fa60d8f61175334.js +1 -0
  79. airflow/providers/fab/www/static/dist/moment.624b1f00ba723d39ce06.js +2 -0
  80. airflow/providers/fab/www/static/dist/moment.624b1f00ba723d39ce06.js.LICENSE.txt +11 -0
  81. airflow/providers/fab/www/static/dist/oss-licenses.json +20 -0
  82. airflow/providers/fab/www/static/js/datetime_utils.js +134 -0
  83. airflow/providers/fab/www/static/js/main.js +324 -0
  84. airflow/providers/fab/www/static/sort_asc.png +0 -0
  85. airflow/providers/fab/www/static/sort_both.png +0 -0
  86. airflow/providers/fab/www/static/sort_desc.png +0 -0
  87. airflow/providers/fab/www/templates/airflow/_messages.html +30 -0
  88. airflow/providers/fab/www/templates/airflow/error.html +35 -0
  89. airflow/providers/fab/www/templates/airflow/main.html +78 -0
  90. airflow/providers/fab/www/templates/airflow/traceback.html +53 -0
  91. airflow/providers/fab/www/templates/appbuilder/flash.html +34 -0
  92. airflow/providers/fab/www/templates/appbuilder/index.html +20 -0
  93. airflow/providers/fab/www/templates/appbuilder/navbar.html +60 -0
  94. airflow/providers/fab/www/templates/appbuilder/navbar_menu.html +60 -0
  95. airflow/providers/fab/www/templates/appbuilder/navbar_right.html +64 -0
  96. airflow/providers/fab/www/utils.py +288 -0
  97. airflow/providers/fab/www/views.py +129 -0
  98. airflow/providers/fab/www/webpack.config.js +213 -0
  99. {apache_airflow_providers_fab-1.5.3.dist-info → apache_airflow_providers_fab-2.0.0.dist-info}/METADATA +29 -37
  100. apache_airflow_providers_fab-2.0.0.dist-info/RECORD +125 -0
  101. {apache_airflow_providers_fab-1.5.3.dist-info → apache_airflow_providers_fab-2.0.0.dist-info}/WHEEL +1 -1
  102. airflow/providers/fab/auth_manager/decorators/auth.py +0 -126
  103. apache_airflow_providers_fab-1.5.3.dist-info/RECORD +0 -51
  104. /airflow/providers/fab/{auth_manager/decorators → www}/__init__.py +0 -0
  105. {apache_airflow_providers_fab-1.5.3.dist-info → apache_airflow_providers_fab-2.0.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,11 @@
1
+ //! Copyright (c) JS Foundation and other contributors
2
+
3
+ //! github.com/moment/moment-timezone
4
+
5
+ //! license : MIT
6
+
7
+ //! moment-timezone.js
8
+
9
+ //! moment.js
10
+
11
+ //! version : 0.5.48
@@ -0,0 +1,20 @@
1
+ [
2
+ {
3
+ "name": "moment",
4
+ "version": "2.30.1",
5
+ "author": "Iskren Ivov Chernev <iskren.chernev@gmail.com> (https://github.com/ichernev)",
6
+ "repository": "https://github.com/moment/moment.git",
7
+ "source": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
8
+ "license": "MIT",
9
+ "licenseText": "Copyright (c) JS Foundation and other contributors\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n"
10
+ },
11
+ {
12
+ "name": "moment-timezone",
13
+ "version": "0.5.48",
14
+ "author": "Tim Wood <washwithcare@gmail.com> (http://timwoodcreates.com/)",
15
+ "repository": "https://github.com/moment/moment-timezone.git",
16
+ "source": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz",
17
+ "license": "MIT",
18
+ "licenseText": "The MIT License (MIT)\n\nCopyright (c) JS Foundation and other contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
19
+ }
20
+ ]
@@ -0,0 +1,134 @@
1
+ /*!
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+
20
+ /* global moment, $, document */
21
+ export const defaultFormat = "YYYY-MM-DD, HH:mm:ss";
22
+ export const isoFormatWithoutTZ = "YYYY-MM-DDTHH:mm:ss.SSS";
23
+ export const defaultFormatWithTZ = "YYYY-MM-DD, HH:mm:ss z";
24
+ export const defaultTZFormat = "z (Z)";
25
+ export const dateTimeAttrFormat = "YYYY-MM-DDThh:mm:ssZ";
26
+
27
+ export const TimezoneEvent = "timezone";
28
+
29
+ export function formatTimezone(what) {
30
+ if (what instanceof moment) {
31
+ return what.isUTC() ? "UTC" : what.format(defaultTZFormat);
32
+ }
33
+
34
+ if (what === "UTC") {
35
+ return what;
36
+ }
37
+
38
+ return moment().tz(what).format(defaultTZFormat);
39
+ }
40
+
41
+ export function isoDateToTimeEl(datetime, options) {
42
+ const dateTimeObj = moment(datetime);
43
+
44
+ const addTitle = $.extend({ title: true }, options).title;
45
+
46
+ const el = document.createElement("time");
47
+ el.setAttribute("datetime", dateTimeObj.format());
48
+ if (addTitle) {
49
+ el.setAttribute(
50
+ "title",
51
+ dateTimeObj.isUTC() ? "" : `UTC: ${dateTimeObj.clone().utc().format()}`
52
+ );
53
+ }
54
+ el.innerText = dateTimeObj.format(defaultFormat);
55
+ return el;
56
+ }
57
+
58
+ export const formatDateTime = (datetime) =>
59
+ moment(datetime).format(defaultFormatWithTZ);
60
+
61
+ export const convertAndFormatUTC = (datetime, tz) => {
62
+ let dateTimeObj = moment.utc(datetime);
63
+ if (tz) dateTimeObj = dateTimeObj.tz(tz);
64
+ return dateTimeObj.format(defaultFormatWithTZ);
65
+ };
66
+
67
+ export const secondsToString = (seconds) => {
68
+ const numdays = Math.floor((seconds % 31536000) / 86400);
69
+ const numhours = Math.floor(((seconds % 31536000) % 86400) / 3600);
70
+ const numminutes = Math.floor((((seconds % 31536000) % 86400) % 3600) / 60);
71
+ const numseconds = Math.floor((((seconds % 31536000) % 86400) % 3600) % 60);
72
+ return (
73
+ (numdays > 0 ? numdays + (numdays === 1 ? " day " : " days ") : "") +
74
+ (numhours > 0 ? numhours + (numhours === 1 ? " hour " : " hours ") : "") +
75
+ (numminutes > 0
76
+ ? numminutes + (numminutes === 1 ? " minute " : " minutes ")
77
+ : "") +
78
+ (numseconds > 0
79
+ ? numseconds + (numseconds === 1 ? " second" : " seconds")
80
+ : "")
81
+ );
82
+ };
83
+
84
+ export function updateAllDateTimes() {
85
+ // Called after `moment.tz.setDefault` has changed the default TZ to display.
86
+
87
+ $('time[data-datetime-convert!="false"]').each((_, el) => {
88
+ const $el = $(el);
89
+ const dt = moment($el.attr("datetime"));
90
+ // eslint-disable-next-line no-underscore-dangle
91
+ if (dt._isValid) {
92
+ $el.text(
93
+ dt.format($el.data("with-tz") ? defaultFormatWithTZ : defaultFormat)
94
+ );
95
+ }
96
+ if ($el.attr("title") !== undefined) {
97
+ // If displayed date is not UTC, have the UTC date in a title attribute
98
+ $el.attr("title", dt.isUTC() ? "" : `UTC: ${dt.clone().utc().format()}`);
99
+ }
100
+ });
101
+
102
+ // Update any date-time inputs.
103
+ //
104
+ // Since we have set the default timezone for moment, it will automatically
105
+ // convert it to the new target for us
106
+ $(".datetime input").each((_, el) => {
107
+ // eslint-disable-next-line no-param-reassign
108
+ el.value = moment(el.value).format();
109
+ });
110
+ }
111
+
112
+ export function setDisplayedTimezone(tz) {
113
+ moment.tz.setDefault(tz);
114
+ updateAllDateTimes();
115
+ }
116
+
117
+ // moment will resolve the enddate to now if it is undefined
118
+ export const getDuration = (startDate, endDate) =>
119
+ moment(endDate || undefined).diff(startDate || undefined);
120
+
121
+ export const formatDuration = (dur) => {
122
+ const duration = moment.duration(dur);
123
+ const totalDays = duration.asDays();
124
+ const days = Math.floor(totalDays);
125
+ // .as('milliseconds') is necessary for .format() to work correctly
126
+ return `${days > 0 ? `${days}d` : ""}${moment
127
+ .utc(duration.as("milliseconds"))
128
+ .format("HH:mm:ss")}`;
129
+ };
130
+
131
+ export const approxTimeFromNow = (dur) => {
132
+ const timefromNow = moment(dur);
133
+ return `${timefromNow.fromNow()}`;
134
+ };
@@ -0,0 +1,324 @@
1
+ /*!
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+ /* global $, moment, Airflow, window, localStorage, document, hostName, csrfToken, CustomEvent */
20
+
21
+ import {
22
+ dateTimeAttrFormat,
23
+ formatTimezone,
24
+ isoDateToTimeEl,
25
+ setDisplayedTimezone,
26
+ TimezoneEvent,
27
+ } from "./datetime_utils";
28
+
29
+ window.isoDateToTimeEl = isoDateToTimeEl;
30
+
31
+ /*
32
+ We pull moment in via a webpack entrypoint rather than import
33
+ so that we don't put it in more than a single .js file.
34
+ This "exports" it to be globally available.
35
+ */
36
+ window.moment = Airflow.moment;
37
+
38
+ function displayTime() {
39
+ const now = moment();
40
+ $("#clock")
41
+ .attr("datetime", now.format(dateTimeAttrFormat))
42
+ .html(`${now.format("HH:mm")} <strong>${formatTimezone(now)}</strong>`);
43
+ }
44
+
45
+ function changeDisplayedTimezone(tz) {
46
+ localStorage.setItem("selected-timezone", tz);
47
+
48
+ // dispatch an event that React can listen for
49
+ const event = new CustomEvent(TimezoneEvent, {
50
+ detail: tz,
51
+ });
52
+ document.dispatchEvent(event);
53
+
54
+ setDisplayedTimezone(tz);
55
+ displayTime();
56
+ $("body").trigger({
57
+ type: "airflow.timezone-change",
58
+ timezone: tz,
59
+ });
60
+ }
61
+
62
+ const el = document.createElement("span");
63
+
64
+ export function escapeHtml(text) {
65
+ el.textContent = text;
66
+ return el.innerHTML;
67
+ }
68
+
69
+ window.escapeHtml = escapeHtml;
70
+
71
+ export function convertSecsToHumanReadable(seconds) {
72
+ let processedSeconds = seconds;
73
+ const oriSeconds = seconds;
74
+ const floatingPart = oriSeconds - Math.floor(oriSeconds);
75
+
76
+ processedSeconds = Math.floor(processedSeconds);
77
+
78
+ const secondsPerHour = 60 * 60;
79
+ const secondsPerMinute = 60;
80
+
81
+ const hours = Math.floor(processedSeconds / secondsPerHour);
82
+ processedSeconds -= hours * secondsPerHour;
83
+
84
+ const minutes = Math.floor(processedSeconds / secondsPerMinute);
85
+ processedSeconds -= minutes * secondsPerMinute;
86
+
87
+ let readableFormat = "";
88
+ if (hours > 0) {
89
+ readableFormat += `${hours}Hours `;
90
+ }
91
+ if (minutes > 0) {
92
+ readableFormat += `${minutes}Min `;
93
+ }
94
+ if (processedSeconds + floatingPart > 0) {
95
+ if (Math.floor(oriSeconds) === oriSeconds) {
96
+ readableFormat += `${processedSeconds}Sec`;
97
+ } else {
98
+ processedSeconds += floatingPart;
99
+ readableFormat += `${processedSeconds.toFixed(3)}Sec`;
100
+ }
101
+ }
102
+ return readableFormat;
103
+ }
104
+ window.convertSecsToHumanReadable = convertSecsToHumanReadable;
105
+
106
+ function postAsForm(url, parameters) {
107
+ const form = $("<form></form>");
108
+
109
+ form.attr("method", "POST");
110
+ form.attr("action", url);
111
+
112
+ $.each(parameters || {}, (key, value) => {
113
+ const field = $("<input></input>");
114
+
115
+ field.attr("type", "hidden");
116
+ field.attr("name", key);
117
+ field.attr("value", value);
118
+
119
+ form.append(field);
120
+ });
121
+
122
+ const field = $("<input></input>");
123
+
124
+ field.attr("type", "hidden");
125
+ field.attr("name", "csrf_token");
126
+ field.attr("value", csrfToken);
127
+
128
+ form.append(field);
129
+
130
+ // The form needs to be a part of the document in order for us to be able
131
+ // to submit it.
132
+ $(document.body).append(form);
133
+ form.submit();
134
+ }
135
+
136
+ window.postAsForm = postAsForm;
137
+
138
+ function initializeUITimezone() {
139
+ const local = moment.tz.guess();
140
+
141
+ const selectedTz = localStorage.getItem("selected-timezone");
142
+ const manualTz = localStorage.getItem("chosen-timezone");
143
+
144
+ function setManualTimezone(tz) {
145
+ localStorage.setItem("chosen-timezone", tz);
146
+ if (tz === local && tz === Airflow.serverTimezone) {
147
+ $("#timezone-manual").hide();
148
+ return;
149
+ }
150
+
151
+ $("#timezone-manual a").data("timezone", tz).text(formatTimezone(tz));
152
+ $("#timezone-manual").show();
153
+ }
154
+
155
+ if (manualTz) {
156
+ setManualTimezone(manualTz);
157
+ }
158
+
159
+ changeDisplayedTimezone(selectedTz || Airflow.defaultUITimezone);
160
+
161
+ if (Airflow.serverTimezone !== "UTC") {
162
+ $("#timezone-server a").html(
163
+ `${formatTimezone(
164
+ Airflow.serverTimezone
165
+ )} <span class="label label-primary">Server</span>`
166
+ );
167
+ $("#timezone-server").show();
168
+ }
169
+
170
+ if (Airflow.serverTimezone !== local) {
171
+ $("#timezone-local a")
172
+ .attr("data-timezone", local)
173
+ .html(
174
+ `${formatTimezone(local)} <span class="label label-info">Local</span>`
175
+ );
176
+ } else {
177
+ $("#timezone-local").hide();
178
+ }
179
+
180
+ $("a[data-timezone]").click((evt) => {
181
+ changeDisplayedTimezone($(evt.currentTarget).data("timezone"));
182
+ });
183
+ // Prepare the data source
184
+ const timezoneData = moment.tz.names().map((tzName) => {
185
+ const category = tzName.split("/", 1)[0];
186
+ return { category, label: tzName.replace("_", " "), value: tzName };
187
+ });
188
+
189
+ // Create a custom filter function to include categories
190
+ function filterByCategory(array, term) {
191
+ const matcher = new RegExp($.ui.autocomplete.escapeRegex(term), "i");
192
+ return $.grep(
193
+ array,
194
+ (item) => matcher.test(item.label) || matcher.test(item.category)
195
+ );
196
+ }
197
+
198
+ // Initialize jQuery UI Autocomplete
199
+ // eslint-disable-next-line no-underscore-dangle
200
+ $("#timezone-other")
201
+ .autocomplete({
202
+ source: (request, response) => {
203
+ const results = filterByCategory(timezoneData, request.term);
204
+ response(results);
205
+ },
206
+ appendTo: "#timezone-menu > li:nth-child(6) > form",
207
+ focus: (event, ui) => {
208
+ // Prevent the value from being inserted on focus
209
+ event.preventDefault();
210
+ $(this).val(ui.item.label);
211
+ },
212
+ select: (event, ui) => {
213
+ // Clear it for next time we open the pop-up
214
+ $(this).val("");
215
+
216
+ setManualTimezone(ui.item.value);
217
+ changeDisplayedTimezone(ui.item.value);
218
+
219
+ return false;
220
+ },
221
+ })
222
+ .data("ui-autocomplete")._renderItem = function (ul, item) {
223
+ const $li = $("<li>");
224
+ $li.append(
225
+ `<a class='dropdown-item' href='#' role='option'>${item.label}</a>`
226
+ );
227
+ return $li.appendTo(ul);
228
+ };
229
+
230
+ // Custom rendering function to include category headers
231
+ // eslint-disable-next-line no-underscore-dangle
232
+ $.ui.autocomplete.prototype._renderMenu = function (ul, items) {
233
+ let currentCategory = "";
234
+ ul.addClass("typeahead dropdown-menu");
235
+ ul.attr("role", "listbox");
236
+ $.each(items, (index, item) => {
237
+ if (item.category !== currentCategory) {
238
+ ul.append(
239
+ `<li class='ui-autocomplete-category dropdown-header'>${item.category}</li>`
240
+ );
241
+ currentCategory = item.category;
242
+ }
243
+ // eslint-disable-next-line no-underscore-dangle
244
+ this._renderItemData(ul, item);
245
+ });
246
+ };
247
+ }
248
+
249
+ function filterOpSelected(ele) {
250
+ const op = $(ele);
251
+ const filterVal = $(".filter_val.form-control", op.parents("tr"));
252
+
253
+ if (op.text() === "Is Null" || op.text() === "Is not Null") {
254
+ if (filterVal.attr("required") !== undefined) {
255
+ filterVal.removeAttr("required");
256
+ filterVal.attr("airflow-required", true);
257
+ }
258
+
259
+ if (filterVal.parent(".datetime").length === 1) {
260
+ filterVal.parent(".datetime").hide();
261
+ } else {
262
+ filterVal.hide();
263
+ }
264
+ } else {
265
+ if (filterVal.attr("airflow-required") === "true") {
266
+ filterVal.attr("required", true);
267
+ filterVal.removeAttr("airflow-required");
268
+ }
269
+
270
+ if (filterVal.parent(".datetime").length === 1) {
271
+ filterVal.parent(".datetime").show();
272
+ } else {
273
+ filterVal.show();
274
+ }
275
+ }
276
+ }
277
+
278
+ $(document).ready(() => {
279
+ initializeUITimezone();
280
+
281
+ $("#clock")
282
+ .attr("data-original-title", hostName)
283
+ .attr("data-placement", "bottom")
284
+ .parent()
285
+ .show();
286
+
287
+ displayTime();
288
+ setInterval(displayTime, 1000);
289
+
290
+ $.ajaxSetup({
291
+ beforeSend(xhr, settings) {
292
+ if (
293
+ !/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) &&
294
+ !this.crossDomain
295
+ ) {
296
+ xhr.setRequestHeader("X-CSRFToken", csrfToken);
297
+ }
298
+ },
299
+ });
300
+
301
+ $.fn.datetimepicker.defaults.sideBySide = true;
302
+ $(".datetimepicker").datetimepicker({ format: "YYYY-MM-DDTHH:mm:ssZ" });
303
+ $(".datepicker").datetimepicker({ format: "YYYY-MM-DD" });
304
+ $(".timepicker").datetimepicker({ format: "HH:mm:ss" });
305
+
306
+ $(".filters .select2-chosen").each((idx, elem) => {
307
+ filterOpSelected(elem);
308
+ });
309
+ $(".filters .select2-chosen").on("DOMNodeInserted", (e) => {
310
+ filterOpSelected(e.target);
311
+ });
312
+
313
+ // Fix up filter fields from FAB adds to the page. This event is fired after
314
+ // the FAB registered one which adds the new control
315
+ $("#filter_form a.filter").click(() => {
316
+ $(".datetimepicker").datetimepicker();
317
+ $(".filters .select2-chosen").on("DOMNodeInserted", (e) => {
318
+ filterOpSelected(e.target);
319
+ });
320
+ });
321
+
322
+ // Global Tooltip selector
323
+ $(".js-tooltip").tooltip();
324
+ });
File without changes
@@ -0,0 +1,30 @@
1
+ {#
2
+ Licensed to the Apache Software Foundation (ASF) under one
3
+ or more contributor license agreements. See the NOTICE file
4
+ distributed with this work for additional information
5
+ regarding copyright ownership. The ASF licenses this file
6
+ to you under the Apache License, Version 2.0 (the
7
+ "License"); you may not use this file except in compliance
8
+ with the License. You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing,
13
+ software distributed under the License is distributed on an
14
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ KIND, either express or implied. See the License for the
16
+ specific language governing permissions and limitations
17
+ under the License.
18
+ #}
19
+ {%- macro show_message(content, category='info', dismissible=true) -%}
20
+ <div class="alert alert-{{ category }}">
21
+ {%- if dismissible -%}
22
+ <button type="button" class="close" data-dismiss="alert">&times;</button>
23
+ {%- endif -%}
24
+ {%- if caller is defined -%}
25
+ {{ caller() }}
26
+ {%- else -%}
27
+ {{ content }}
28
+ {%- endif -%}
29
+ </div>
30
+ {%- endmacro -%}
@@ -0,0 +1,35 @@
1
+ {#
2
+ Licensed to the Apache Software Foundation (ASF) under one
3
+ or more contributor license agreements. See the NOTICE file
4
+ distributed with this work for additional information
5
+ regarding copyright ownership. The ASF licenses this file
6
+ to you under the Apache License, Version 2.0 (the
7
+ "License"); you may not use this file except in compliance
8
+ with the License. You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing,
13
+ software distributed under the License is distributed on an
14
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ KIND, either express or implied. See the License for the
16
+ specific language governing permissions and limitations
17
+ under the License.
18
+ #}
19
+
20
+ <!DOCTYPE html>
21
+ <html lang="en">
22
+ <head>
23
+ <title>Airflow {{ status_code }}</title>
24
+ <link rel="icon" type="image/png" href="{{ url_for('static', filename='pin_32.png') }}">
25
+ </head>
26
+ <body>
27
+ <div style="font-family: verdana; text-align: center; margin-top: 200px;">
28
+ <img src="{{ url_for('static', filename='pin_100.png') }}" width="50px" alt="pin-logo" />
29
+ <h1>Airflow {{ status_code }}</h1>
30
+ <p>{{ error_message }}</p>
31
+ <a href="/">Return to the main page</a>
32
+ <p>{{ hostname }}</p>
33
+ </div>
34
+ </body>
35
+ </html>
@@ -0,0 +1,78 @@
1
+ {#
2
+ Licensed to the Apache Software Foundation (ASF) under one
3
+ or more contributor license agreements. See the NOTICE file
4
+ distributed with this work for additional information
5
+ regarding copyright ownership. The ASF licenses this file
6
+ to you under the Apache License, Version 2.0 (the
7
+ "License"); you may not use this file except in compliance
8
+ with the License. You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing,
13
+ software distributed under the License is distributed on an
14
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ KIND, either express or implied. See the License for the
16
+ specific language governing permissions and limitations
17
+ under the License.
18
+ #}
19
+
20
+ {% extends 'appbuilder/baselayout.html' %}
21
+ {% from 'airflow/_messages.html' import show_message %}
22
+
23
+ {% block page_title -%}
24
+ Airflow
25
+ {% endblock %}
26
+
27
+ {% block head_css %}
28
+ {{ super() }}
29
+
30
+ {% if not appbuilder.app_theme %}
31
+ {# airflowDefaultTheme.css file contains the styles from local bootstrap-theme.css #}
32
+ <link rel="stylesheet" type="text/css" href="{{ url_for_asset('airflowDefaultTheme.css') }}">
33
+ {% endif %}
34
+ <link rel="stylesheet" type="text/css" href="{{ url_for_asset('materialIcons.css') }}">
35
+ <link rel="stylesheet" type="text/css" href="{{ url_for_asset('main.css') }}">
36
+ <link rel="stylesheet" type="text/css" href="{{ url_for_asset('loadingDots.css') }}">
37
+ <link rel="stylesheet" type="text/css" href="{{ url_for_asset('jquery-ui.min.css') }}">
38
+ <style type="text/css">
39
+ {% for state, state_color in state_color_mapping.items() %}
40
+ span.{{state}} {
41
+ background-color: {{state_color}};
42
+ }
43
+ {% endfor %}
44
+ .navbar-nav > li > a {
45
+ color: {{ navbar_text_color }};
46
+ }
47
+ .navbar-nav > li > a:hover {
48
+ background-color: {{ navbar_hover_color }};
49
+ color: {{ navbar_text_hover_color }};
50
+ }
51
+ </style>
52
+ <link rel="icon" type="image/png" href="{{ url_for('static', filename='pin_32.png') }}">
53
+ {% endblock %}
54
+
55
+ {% block messages %}
56
+ {% include 'appbuilder/flash.html' %}
57
+ {% if show_plugin_message %}
58
+ {% call show_message(category='warning', dismissible=false) %}
59
+ <p>
60
+ You have a plugin that is using a FAB view or Flask Blueprint, which was used for the Airflow 2 UI, and is now
61
+ deprecated. Please update your plugin to be compatible with the Airflow 3 UI.
62
+ </p>
63
+ {% endcall %}
64
+ {% endif %}
65
+ {% endblock %}
66
+
67
+ {% block tail_js %}
68
+ {{ super() }}
69
+ <script>
70
+ // below variables are used in main.js
71
+ // keep as var, changing to const or let breaks other code
72
+ var hostName = '{{ hostname }}';
73
+ $('time[title]').tooltip();
74
+ </script>
75
+ <script src="{{ url_for_asset('moment.js') }}"></script>
76
+ <script src="{{ url_for_asset('jquery-ui.min.js') }}"></script>
77
+ <script src="{{ url_for_asset('main.js') }}"></script>
78
+ {% endblock %}