Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions airflow/www/templates/airflow/dag.html
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,12 @@ <h4 class="js-dataset-triggered" style="user-select: none;-moz-user-select: auto
<span class="material-icons" aria-hidden="true">play_arrow</span>
</a>
{% endif %}
<a href="{{ url_for('Airflow.parse_dag', file_token=dag_file_token, redirect_url=url_for(request.endpoint, dag_id=dag.dag_id)) }}"
title="Reparse&nbsp;DAG"
aria-label="Reparse DAG"
class="btn btn-default btn-icon-only {{ ' disabled' if not dag.can_edit }}">
<span class="material-icons">restore_page</span>
</a>
<a href="{{ url_for('Airflow.delete', dag_id=dag.dag_id, redirect_url=url_for(request.endpoint, dag_id=dag.dag_id)) }}"
title="Delete&nbsp;DAG"
aria-label="Delete DAG"
Expand Down
4 changes: 4 additions & 0 deletions airflow/www/templates/airflow/dags.html
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,10 @@ <h2>{{ page_title }}</h2>
<span class="material-icons" aria-hidden="true">play_arrow</span>
</a>
{% endif %}
<a href="{{ url_for('Airflow.parse_dag', file_token=file_tokens[dag.dag_id], redirect_url=url_for(request.endpoint)) }}" title="Reparse&nbsp;DAG" aria-label="Reparse DAG"
class="btn btn-sm btn-default btn-icon-only{{ ' disabled' if not dag.can_edit }}">
<span class="material-icons">restore_page</span>
</a>
<a href="{{ url_for('Airflow.delete', dag_id=dag.dag_id, redirect_url=url_for(request.endpoint)) }}"
onclick="return confirmDeleteDag(this, '{{ dag.dag_id }}')" title="Delete&nbsp;DAG"
aria-label="Delete DAG"
Expand Down
28 changes: 28 additions & 0 deletions airflow/www/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
Response,
abort,
before_render_template,
current_app,
flash,
g,
has_request_context,
Expand All @@ -67,6 +68,7 @@
from flask_appbuilder.urltools import get_order_args, get_page_args, get_page_size_args
from flask_appbuilder.widgets import FormWidget
from flask_babel import lazy_gettext
from itsdangerous import URLSafeSerializer
from jinja2.utils import htmlsafe_json_dumps, pformat # type: ignore
from markupsafe import Markup, escape
from pendulum.datetime import DateTime
Expand Down Expand Up @@ -739,6 +741,8 @@ def render_template(self, *args, **kwargs):
kwargs["can_edit_dag"] = get_auth_manager().is_authorized_dag(
method="PUT", details=DagDetails(id=kwargs["dag"].dag_id)
)
url_serializer = URLSafeSerializer(current_app.config["SECRET_KEY"])
kwargs["dag_file_token"] = url_serializer.dumps(kwargs["dag"].fileloc)

return super().render_template(
*args,
Expand Down Expand Up @@ -957,6 +961,7 @@ def index(self):
else:
dataset_triggered_next_run_info = {}

file_tokens = {}
for dag in dags:
dag.can_edit = get_auth_manager().is_authorized_dag(
method="PUT", details=DagDetails(id=dag.dag_id), user=g.user
Expand All @@ -965,6 +970,8 @@ def index(self):
dag.can_delete = get_auth_manager().is_authorized_dag(
method="DELETE", details=DagDetails(id=dag.dag_id), user=g.user
)
url_serializer = URLSafeSerializer(current_app.config["SECRET_KEY"])
file_tokens[dag.dag_id] = url_serializer.dumps(dag.fileloc)

dagtags = session.execute(select(func.distinct(DagTag.name)).order_by(DagTag.name)).all()
tags = [
Expand Down Expand Up @@ -1106,6 +1113,7 @@ def _iter_parsed_moved_data_table_names():
auto_refresh_interval=conf.getint("webserver", "auto_refresh_interval"),
dataset_triggered_next_run_info=dataset_triggered_next_run_info,
scarf_url=scarf_url,
file_tokens=file_tokens,
)

@expose("/datasets")
Expand Down Expand Up @@ -1404,6 +1412,7 @@ def rendered_templates(self, session):
raw_task = dag.get_task(task_id).prepare_for_execution()

no_dagrun = False
url_serializer = URLSafeSerializer(current_app.config["SECRET_KEY"])

title = "Rendered Template"
html_dict = {}
Expand Down Expand Up @@ -1504,6 +1513,7 @@ def rendered_templates(self, session):
form=form,
root=root,
title=title,
dag_file_token=url_serializer.dumps(dag.fileloc),
)

@expose("/rendered-k8s")
Expand Down Expand Up @@ -2880,6 +2890,7 @@ def grid(self, dag_id: str, session: Session = NEW_SESSION):
color_log_warning_keywords = conf.get("logging", "color_log_warning_keywords", fallback="")

dag = get_airflow_app().dag_bag.get_dag(dag_id, session=session)
url_serializer = URLSafeSerializer(current_app.config["SECRET_KEY"])
dag_model = DagModel.get_dagmodel(dag_id, session=session)
if not dag:
flash(f'DAG "{dag_id}" seems to be missing from DagBag.', "error")
Expand Down Expand Up @@ -2938,6 +2949,7 @@ def grid(self, dag_id: str, session: Session = NEW_SESSION):
excluded_events_raw=excluded_events_raw,
color_log_error_keywords=color_log_error_keywords,
color_log_warning_keywords=color_log_warning_keywords,
dag_file_token=url_serializer.dumps(dag.fileloc),
)

@expose("/calendar")
Expand Down Expand Up @@ -3591,6 +3603,22 @@ def audit_log(self, dag_id: str):

return redirect(url_for("Airflow.grid", **kwargs))

@expose("/parseDagFile/<string:file_token>")
def parse_dag(self, file_token: str):
from airflow.api_connexion.endpoints.dag_parsing import reparse_dag_file

with create_session() as session:
response = reparse_dag_file(file_token=file_token, session=session)
response_messages = {
201: ["Reparsing request submitted successfully", "info"],
401: ["Unauthenticated request", "error"],
403: ["Permission Denied", "error"],
404: ["DAG not found", "error"],
}
flash(response_messages[response.status_code][0], response_messages[response.status_code][1])
redirect_url = get_safe_url(request.values.get("redirect_url"))
return redirect(redirect_url)


class ConfigurationView(AirflowBaseView):
"""View to show Airflow Configurations."""
Expand Down