Skip to content

Commit

Permalink
Add read only validation to read only fields (#33413)
Browse files Browse the repository at this point in the history
* Add read only validation to read only fields

Add read only validation to DagRunEditForm and TaskInstanceEditForm
read only fields.

* Improve docstring

---------

Co-authored-by: Hussein Awala <hussein@awala.fr>
  • Loading branch information
ahidalgob and hussein-awala authored Aug 16, 2023
1 parent 843a3b8 commit b7a46c9
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 16 deletions.
54 changes: 38 additions & 16 deletions airflow/www/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
from airflow.providers_manager import ProvidersManager
from airflow.utils import timezone
from airflow.utils.types import DagRunType
from airflow.www.validators import ValidKey
from airflow.www.validators import ReadOnly, ValidKey
from airflow.www.widgets import (
AirflowDateTimePickerROWidget,
AirflowDateTimePickerWidget,
Expand Down Expand Up @@ -121,38 +121,54 @@ class DateTimeWithNumRunsForm(FlaskForm):
class DagRunEditForm(DynamicForm):
"""Form for editing DAG Run.
We don't actually want to allow editing, so everything is read-only here.
Only note field is editable, so everything else is read-only here.
"""

dag_id = StringField(lazy_gettext("Dag Id"), widget=BS3TextFieldROWidget())
start_date = DateTimeWithTimezoneField(lazy_gettext("Start Date"), widget=AirflowDateTimePickerROWidget())
end_date = DateTimeWithTimezoneField(lazy_gettext("End Date"), widget=AirflowDateTimePickerROWidget())
run_id = StringField(lazy_gettext("Run Id"), widget=BS3TextFieldROWidget())
state = StringField(lazy_gettext("State"), widget=BS3TextFieldROWidget())
dag_id = StringField(lazy_gettext("Dag Id"), validators=[ReadOnly()], widget=BS3TextFieldROWidget())
start_date = DateTimeWithTimezoneField(
lazy_gettext("Start Date"), validators=[ReadOnly()], widget=AirflowDateTimePickerROWidget()
)
end_date = DateTimeWithTimezoneField(
lazy_gettext("End Date"), validators=[ReadOnly()], widget=AirflowDateTimePickerROWidget()
)
run_id = StringField(lazy_gettext("Run Id"), validators=[ReadOnly()], widget=BS3TextFieldROWidget())
state = StringField(lazy_gettext("State"), validators=[ReadOnly()], widget=BS3TextFieldROWidget())
execution_date = DateTimeWithTimezoneField(
lazy_gettext("Logical Date"),
validators=[ReadOnly()],
widget=AirflowDateTimePickerROWidget(),
)
conf = TextAreaField(lazy_gettext("Conf"), widget=BS3TextAreaROWidget())
conf = TextAreaField(lazy_gettext("Conf"), validators=[ReadOnly()], widget=BS3TextAreaROWidget())
note = TextAreaField(lazy_gettext("User Note"), widget=BS3TextAreaFieldWidget())

def populate_obj(self, item):
"""Populates the attributes of the passed obj with data from the form's fields."""
super().populate_obj(item)
"""Populates the attributes of the passed obj with data from the form's not-read-only fields."""
for name, field in self._fields.items():
if not field.flags.readonly:
field.populate_obj(item, name)
item.run_type = DagRunType.from_run_id(item.run_id)
if item.conf:
item.conf = json.loads(item.conf)


class TaskInstanceEditForm(DynamicForm):
"""Form for editing TaskInstance."""
"""Form for editing TaskInstance.
dag_id = StringField(lazy_gettext("Dag Id"), validators=[InputRequired()], widget=BS3TextFieldROWidget())
Only note and state fields are editable, so everything else is read-only here.
"""

dag_id = StringField(
lazy_gettext("Dag Id"), validators=[InputRequired(), ReadOnly()], widget=BS3TextFieldROWidget()
)
task_id = StringField(
lazy_gettext("Task Id"), validators=[InputRequired()], widget=BS3TextFieldROWidget()
lazy_gettext("Task Id"), validators=[InputRequired(), ReadOnly()], widget=BS3TextFieldROWidget()
)
start_date = DateTimeWithTimezoneField(
lazy_gettext("Start Date"), validators=[ReadOnly()], widget=AirflowDateTimePickerROWidget()
)
end_date = DateTimeWithTimezoneField(
lazy_gettext("End Date"), validators=[ReadOnly()], widget=AirflowDateTimePickerROWidget()
)
start_date = DateTimeWithTimezoneField(lazy_gettext("Start Date"), widget=AirflowDateTimePickerROWidget())
end_date = DateTimeWithTimezoneField(lazy_gettext("End Date"), widget=AirflowDateTimePickerROWidget())
state = SelectField(
lazy_gettext("State"),
choices=(
Expand All @@ -167,10 +183,16 @@ class TaskInstanceEditForm(DynamicForm):
execution_date = DateTimeWithTimezoneField(
lazy_gettext("Logical Date"),
widget=AirflowDateTimePickerROWidget(),
validators=[InputRequired()],
validators=[InputRequired(), ReadOnly()],
)
note = TextAreaField(lazy_gettext("User Note"), widget=BS3TextAreaFieldWidget())

def populate_obj(self, item):
"""Populates the attributes of the passed obj with data from the form's not-read-only fields."""
for name, field in self._fields.items():
if not field.flags.readonly:
field.populate_obj(item, name)


@cache
def create_connection_form_class() -> type[DynamicForm]:
Expand Down
11 changes: 11 additions & 0 deletions airflow/www/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,14 @@ def __call__(self, form, field):
helpers.validate_key(field.data, self.max_length)
except Exception as e:
raise ValidationError(str(e))


class ReadOnly:
"""Adds readonly flag to a field.
When using this you normally will need to override the form's populate_obj method,
so field.populate_obj is not called for read-only fields.
"""

def __call__(self, form, field):
field.flags.readonly = True
11 changes: 11 additions & 0 deletions tests/www/test_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,14 @@ def test_validation_fails_with_too_many_characters(self):
match=r"The key has to be less than [0-9]+ characters",
):
self._validate()


class TestReadOnly:
def setup_method(self):
self.form_read_only_field_mock = mock.MagicMock(data="readOnlyField")
self.form_mock = mock.MagicMock(spec_set=dict)

def test_read_only_validator(self):
validator = validators.ReadOnly()
assert validator(self.form_mock, self.form_read_only_field_mock) is None
assert self.form_read_only_field_mock.flags.readonly is True

0 comments on commit b7a46c9

Please sign in to comment.