Skip to content

Commit 66eb282

Browse files
authored
Cleanup and do housekeeping with plugin examples (apache#28537)
This PR performs housekeeping of the plugin examples: * makes the examples independent of Hive being installed * adds "has_access" in the examples * removes the misleading "metastore" (which is hive metastore not Airflow Metastore as used in other places This way our example will be much easier to apply by anyone.
1 parent 0ebc62f commit 66eb282

File tree

13 files changed

+109
-530
lines changed

13 files changed

+109
-530
lines changed

.dockerignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
!chart
3535
!docs
3636
!licenses
37-
!metastore_browser
3837

3938
# Add those folders to the context so that they are available in the CI container
4039
!scripts/in_container

dev/breeze/src/airflow_breeze/utils/docker_command_utils.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@
104104
("kubernetes_tests", "/opt/airflow/kubernetes_tests"),
105105
("docker_tests", "/opt/airflow/docker_tests"),
106106
("chart", "/opt/airflow/chart"),
107-
("metastore_browser", "/opt/airflow/metastore_browser"),
108107
]
109108

110109

dev/breeze/tests/test_commands.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,23 +27,23 @@ def test_visuals():
2727

2828
def test_get_extra_docker_flags_all():
2929
flags = get_extra_docker_flags(MOUNT_ALL)
30-
assert "empty" not in "".join(flags)
30+
assert "/empty," not in "".join(flags)
3131
assert len(flags) < 10
3232

3333

3434
def test_get_extra_docker_flags_selected():
3535
flags = get_extra_docker_flags(MOUNT_SELECTED)
36-
assert "empty" not in "".join(flags)
36+
assert "/empty," not in "".join(flags)
3737
assert len(flags) > 40
3838

3939

4040
def test_get_extra_docker_flags_remove():
4141
flags = get_extra_docker_flags(MOUNT_REMOVE)
42-
assert "empty" in "".join(flags)
42+
assert "/empty," in "".join(flags)
4343
assert len(flags) < 10
4444

4545

4646
def test_get_extra_docker_flags_skip():
4747
flags = get_extra_docker_flags(MOUNT_SKIP)
48-
assert "empty" not in "".join(flags)
48+
assert "/empty," not in "".join(flags)
4949
assert len(flags) < 10

docs/apache-airflow/authoring-and-scheduling/plugins.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ definitions in Airflow.
168168
169169
# This is the class you derive to create a plugin
170170
from airflow.plugins_manager import AirflowPlugin
171+
from airflow.security import permissions
172+
from airflow.www.auth import has_access
171173
172174
from flask import Blueprint
173175
from flask_appbuilder import expose, BaseView as AppBuilderBaseView
@@ -201,6 +203,11 @@ definitions in Airflow.
201203
default_view = "test"
202204
203205
@expose("/")
206+
@has_access(
207+
[
208+
(permissions.ACTION_CAN_READ, permissions.RESOURCE_WEBSITE),
209+
]
210+
)
204211
def test(self):
205212
return self.render_template("test_plugin/test.html", content="Hello galaxy!")
206213
@@ -210,6 +217,11 @@ definitions in Airflow.
210217
default_view = "test"
211218
212219
@expose("/")
220+
@has_access(
221+
[
222+
(permissions.ACTION_CAN_READ, permissions.RESOURCE_WEBSITE),
223+
]
224+
)
213225
def test(self):
214226
return self.render_template("test_plugin/test.html", content="Hello galaxy!")
215227

metastore_browser/README.md renamed to docs/apache-airflow/empty_plugin/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
under the License.
1818
-->
1919

20-
# Apache Hive metastore plugin
20+
# Apache example plugin
2121

2222
This is an example plugin for Apache Airflow.
2323

24-
This plugin allows you to view Apache Hive metastore from the web UI interface.
24+
This plugin displays empty view.
2525

2626
## Installation
2727

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
"""Plugins example"""
19+
from __future__ import annotations
20+
21+
from flask import Blueprint
22+
from flask_appbuilder import BaseView, expose
23+
24+
from airflow.plugins_manager import AirflowPlugin
25+
from airflow.security import permissions
26+
from airflow.www.auth import has_access
27+
28+
29+
class EmptyPluginView(BaseView):
30+
"""Creating a Flask-AppBuilder View"""
31+
32+
default_view = "index"
33+
34+
@expose("/")
35+
@has_access(
36+
[
37+
(permissions.ACTION_CAN_READ, permissions.RESOURCE_WEBSITE),
38+
]
39+
)
40+
def index(self):
41+
"""Create default view"""
42+
return self.render_template("empty_plugin/index.html", name="Empty Plugin")
43+
44+
45+
# Creating a flask blueprint
46+
bp = Blueprint(
47+
"Empty Plugin",
48+
__name__,
49+
template_folder="templates",
50+
static_folder="static",
51+
static_url_path="/static/empty_plugin",
52+
)
53+
54+
55+
class EmptyPlugin(AirflowPlugin):
56+
"""Defining the plugin class"""
57+
58+
name = "Empty Plugin"
59+
flask_blueprints = [bp]
60+
appbuilder_views = [{"name": "Empty Plugin", "category": "Extra Views", "view": EmptyPluginView()}]

metastore_browser/templates/metastore_browser/dbs.html renamed to docs/apache-airflow/empty_plugin/templates/empty_plugin/index.html

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,23 @@
1717
under the License.
1818
#}
1919

20-
{% extends 'metastore_browser/base.html' %}
20+
{% extends base_template %}
2121

22-
{% block plugin_content %}
23-
<h4>
24-
<span>Hive Databases</span>
25-
</h4>
26-
{{ table }}
22+
{% block head %}
23+
{{ super() }}
24+
{% endblock %}
25+
26+
{% block body %}
27+
<div>
28+
<h3 style="float: left">
29+
{% block page_header %}{{ name }}{% endblock%}
30+
</h3>
31+
<div id="object" class="select2-drop-mask" style="margin-top: 25px; width: 400px;float: right"></div>
32+
<div style="clear: both"></div>
33+
</div>
34+
{% block plugin_content %}{% endblock %}
35+
{% endblock %}
36+
37+
{% block tail %}
38+
{{ super() }}
2739
{% endblock %}

docs/apache-airflow/howto/custom-view-plugin.rst

Lines changed: 13 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,22 @@
1616
under the License.
1717
1818
19-
Customize view of Apache Hive Metastore from Airflow web UI
20-
===========================================================
19+
Customize view of Apache from Airflow web UI
20+
============================================
2121

2222
Airflow has feature that allows to integrate a custom UI along with its
2323
core UI using the Plugin manager
2424

25-
This is an example plugin for Airflow that allows to create custom view of
26-
Apache Hive metastore from the web UI of Airflow. Showing Metastore information
27-
like list of the tables in database, finding the table object/ ddl information
28-
for given table, retrieving partition information, retrieving data from the table,
29-
retrieving table objects from Hive Metastore are some of the custom views shown
30-
in this example.
25+
This is an example plugin for Airflow that displays absolutely nothing.
3126

3227
In this plugin, two object reference are derived from the base class
3328
``airflow.plugins_manager.AirflowPlugin``. They are flask_blueprints and
3429
appbuilder_views
3530

3631
Using flask_blueprints in Airflow plugin, the core application can be extended
37-
to support the application that is customized to view Apache Hive Metastore.
32+
to support the application that is customized to view Empty Plugin.
3833
In this object reference, the list of Blueprint object with the static template for
39-
rendering the Metastore information is passed on.
34+
rendering the information.
4035

4136
Using appbuilder_views in Airflow plugin, a class that represents a concept is
4237
added and presented with views and methods to implement it.
@@ -48,73 +43,32 @@ Custom view Registration
4843
------------------------
4944

5045
A custom view with object reference to flask_appbuilder and Blueprint from flask
51-
and be registered as a part of a :doc:`plugin </authoring-and-scheduling/plugins>`. The following is a
52-
skeleton for us to implement a new custom view:
46+
and be registered as a part of a :doc:`plugin </authoring-and-scheduling/plugins>`.
5347

54-
.. code-block:: python
55-
56-
from airflow.plugins_manager import AirflowPlugin
57-
from flask import Blueprint
58-
from flask_appbuilder import BaseView
59-
60-
61-
class MetastoreBrowserView(BaseView):
62-
pass
63-
64-
65-
# Creating a flask blueprint to integrate the templates and static folder
66-
bp = Blueprint(
67-
"metastore_browser",
68-
__name__,
69-
template_folder="templates",
70-
static_folder="static",
71-
static_url_path="/static/metastore_browser",
72-
)
48+
The following is a skeleton for us to implement a new custom view:
7349

50+
.. exampleinclude:: ../empty_plugin/empty_plugin.py
51+
:language: python
7452

75-
class MetastoreBrowserPlugin(AirflowPlugin):
76-
name = "metastore_browser"
77-
flask_blueprints = [bp]
78-
appbuilder_views = [
79-
{
80-
"category": "Plugins", # name of the tab in Airflow UI
81-
"name": "Hive Metadata Browser", # name of link under the tab
82-
"view": MetastoreBrowserView(),
83-
}
84-
]
8553

8654
``Plugins`` specified in the ``category`` key of ``appbuilder_views`` dictionary is
87-
the name of the tab in the navigation bar of the Airflow UI. ``Hive Metastore Browser``
55+
the name of the tab in the navigation bar of the Airflow UI. ``Empty Plugin``
8856
is the name of the link under the tab ``Plugins``, which will launch the plugin
8957

9058
We have to add Blueprint for generating the part of the application
9159
that needs to be rendered in Airflow web UI. We can define templates, static files
9260
and this blueprint will be registered as part of the Airflow application when the
9361
plugin gets loaded.
9462

95-
Next, we can add code into ``MetastoreBrowserView`` with views and implementing
96-
methods for each of those views. After the implementation, the custom view
97-
created becomes part of the Airflow web UI.
98-
99-
For reference, here's the plugin code within ``MetastoreBrowserView`` class that shows list of tables in the database:
100-
101-
.. exampleinclude:: ../../../metastore_browser/hive_metastore.py
102-
:language: python
103-
:start-after: [START howto_customview_show_database_table]
104-
:end-before: [END howto_customview_show_database_table]
105-
10663
The ``$AIRFLOW_HOME/plugins`` folder with custom view UI have the following folder structure.
10764

10865
::
10966

11067
plugins
111-
├── hive_metastore.py
68+
├── empty_plugin.py
11269
├── templates
113-
| └── metastore_browser
114-
| ├── base.html
115-
| ├── db.html
116-
| ├── dbs.html
117-
│ └── table.html
70+
| └── empty_plugin
71+
| ├── index.html
11872
└── README.md
11973

12074
The HTML files required to render the views built is added as part of the

0 commit comments

Comments
 (0)