cornflow 1.2.3a5__py3-none-any.whl → 1.3.0rc1__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 (57) hide show
  1. cornflow/app.py +24 -8
  2. cornflow/cli/service.py +93 -43
  3. cornflow/commands/auxiliar.py +4 -1
  4. cornflow/commands/dag.py +7 -7
  5. cornflow/commands/permissions.py +13 -7
  6. cornflow/commands/views.py +3 -0
  7. cornflow/config.py +26 -6
  8. cornflow/endpoints/__init__.py +27 -0
  9. cornflow/endpoints/case.py +37 -21
  10. cornflow/endpoints/dag.py +5 -5
  11. cornflow/endpoints/data_check.py +8 -7
  12. cornflow/endpoints/example_data.py +4 -2
  13. cornflow/endpoints/execution.py +215 -127
  14. cornflow/endpoints/health.py +30 -11
  15. cornflow/endpoints/instance.py +3 -3
  16. cornflow/endpoints/login.py +9 -2
  17. cornflow/endpoints/permission.py +1 -2
  18. cornflow/endpoints/schemas.py +3 -3
  19. cornflow/endpoints/signup.py +17 -11
  20. cornflow/migrations/versions/999b98e24225.py +34 -0
  21. cornflow/migrations/versions/cef1df240b27_.py +34 -0
  22. cornflow/models/__init__.py +2 -1
  23. cornflow/models/dag.py +8 -9
  24. cornflow/models/dag_permissions.py +3 -3
  25. cornflow/models/execution.py +2 -3
  26. cornflow/models/permissions.py +1 -0
  27. cornflow/models/user.py +1 -1
  28. cornflow/schemas/execution.py +14 -1
  29. cornflow/schemas/health.py +1 -1
  30. cornflow/shared/authentication/auth.py +14 -1
  31. cornflow/shared/authentication/decorators.py +32 -2
  32. cornflow/shared/const.py +58 -1
  33. cornflow/shared/exceptions.py +2 -1
  34. cornflow/tests/base_test_execution.py +798 -0
  35. cornflow/tests/const.py +1 -0
  36. cornflow/tests/integration/test_commands.py +2 -2
  37. cornflow/tests/integration/test_cornflowclient.py +2 -1
  38. cornflow/tests/unit/test_apiview.py +7 -1
  39. cornflow/tests/unit/test_cases.py +1 -1
  40. cornflow/tests/unit/test_cli.py +6 -3
  41. cornflow/tests/unit/test_commands.py +7 -6
  42. cornflow/tests/unit/test_dags.py +3 -3
  43. cornflow/tests/unit/test_example_data.py +1 -1
  44. cornflow/tests/unit/test_executions.py +115 -535
  45. cornflow/tests/unit/test_get_resources.py +103 -0
  46. cornflow/tests/unit/test_health.py +84 -3
  47. cornflow/tests/unit/test_main_alarms.py +1 -1
  48. cornflow/tests/unit/test_roles.py +2 -1
  49. cornflow/tests/unit/test_schema_from_models.py +1 -1
  50. cornflow/tests/unit/test_schemas.py +1 -1
  51. cornflow/tests/unit/test_sign_up.py +181 -6
  52. cornflow/tests/unit/tools.py +93 -10
  53. {cornflow-1.2.3a5.dist-info → cornflow-1.3.0rc1.dist-info}/METADATA +3 -3
  54. {cornflow-1.2.3a5.dist-info → cornflow-1.3.0rc1.dist-info}/RECORD +57 -53
  55. {cornflow-1.2.3a5.dist-info → cornflow-1.3.0rc1.dist-info}/WHEEL +0 -0
  56. {cornflow-1.2.3a5.dist-info → cornflow-1.3.0rc1.dist-info}/entry_points.txt +0 -0
  57. {cornflow-1.2.3a5.dist-info → cornflow-1.3.0rc1.dist-info}/top_level.txt +0 -0
@@ -16,7 +16,7 @@ from werkzeug.utils import secure_filename
16
16
 
17
17
  # Import from internal modules
18
18
  from cornflow.endpoints.meta_resource import BaseMetaResource
19
- from cornflow.models import InstanceModel, DeployedDAG
19
+ from cornflow.models import InstanceModel, DeployedWorkflow
20
20
  from cornflow.schemas.instance import (
21
21
  InstanceSchema,
22
22
  InstanceEndpointResponse,
@@ -91,7 +91,7 @@ class InstanceEndpoint(BaseMetaResource):
91
91
  # We validate the instance data
92
92
  config = current_app.config
93
93
 
94
- instance_schema = DeployedDAG.get_one_schema(
94
+ instance_schema = DeployedWorkflow.get_one_schema(
95
95
  config, data_schema, INSTANCE_SCHEMA
96
96
  )
97
97
  instance_errors = json_schema_validate_as_string(
@@ -166,7 +166,7 @@ class InstanceDetailsEndpoint(InstanceDetailsEndpointBase):
166
166
 
167
167
  config = current_app.config
168
168
 
169
- instance_schema = DeployedDAG.get_one_schema(
169
+ instance_schema = DeployedWorkflow.get_one_schema(
170
170
  config, schema, INSTANCE_SCHEMA
171
171
  )
172
172
  instance_errors = json_schema_validate_as_string(
@@ -188,8 +188,15 @@ class LoginBaseEndpoint(BaseMetaResource):
188
188
  )
189
189
 
190
190
  email = decoded_token.get("email", f"{username}@cornflow.org")
191
- first_name = decoded_token.get("given_name", "")
192
- last_name = decoded_token.get("family_name", "")
191
+
192
+ email_names = email.split("@")[0]
193
+ if "." in email_names:
194
+ default_first_name, default_last_name = email_names.split(".")[0:2]
195
+ else:
196
+ default_first_name, default_last_name = email_names, ""
197
+
198
+ first_name = decoded_token.get("given_name", default_first_name)
199
+ last_name = decoded_token.get("family_name", default_last_name)
193
200
 
194
201
  data = {
195
202
  "username": username,
@@ -1,6 +1,5 @@
1
- """
1
+ """ """
2
2
 
3
- """
4
3
  # Import from libraries
5
4
  from flask_apispec import doc, marshal_with, use_kwargs
6
5
  from flask import current_app
@@ -8,7 +8,7 @@ from flask_apispec import marshal_with, doc
8
8
 
9
9
  # Import from internal modules
10
10
  from cornflow.endpoints.meta_resource import BaseMetaResource
11
- from cornflow.models import PermissionsDAG, DeployedDAG
11
+ from cornflow.models import PermissionsDAG, DeployedWorkflow
12
12
  from cornflow.schemas.schemas import SchemaOneApp, SchemaListApp
13
13
  from cornflow.shared.authentication import Auth, authenticate
14
14
  from cornflow.shared.const import ALL_DEFAULT_ROLES
@@ -63,7 +63,7 @@ class SchemaDetailsEndpoint(BaseMetaResource):
63
63
  )
64
64
 
65
65
  if permission:
66
- deployed_dag = DeployedDAG.get_one_object(dag_name)
66
+ deployed_dag = DeployedWorkflow.get_one_object(dag_name)
67
67
  current_app.logger.info("User gets schema {}".format(dag_name))
68
68
  return {
69
69
  "instance": deployed_dag.instance_schema,
@@ -71,7 +71,7 @@ class SchemaDetailsEndpoint(BaseMetaResource):
71
71
  "instance_checks": deployed_dag.instance_checks_schema,
72
72
  "solution_checks": deployed_dag.solution_checks_schema,
73
73
  "config": deployed_dag.config_schema,
74
- "name": dag_name
74
+ "name": dag_name,
75
75
  }, 200
76
76
  else:
77
77
  err = "User does not have permission to access this dag"
@@ -1,6 +1,7 @@
1
1
  """
2
2
  External endpoint for the user to signup
3
3
  """
4
+
4
5
  # Import from libraries
5
6
  from flask import current_app
6
7
  from flask_apispec import use_kwargs, doc
@@ -9,8 +10,8 @@ from flask_apispec import use_kwargs, doc
9
10
  from cornflow.endpoints.meta_resource import BaseMetaResource
10
11
  from cornflow.models import PermissionsDAG, UserRoleModel, UserModel
11
12
  from cornflow.schemas.user import SignupRequest
12
- from cornflow.shared.authentication import Auth
13
- from cornflow.shared.const import AUTH_LDAP, AUTH_OID
13
+ from cornflow.shared.authentication import Auth, authenticate
14
+ from cornflow.shared.const import AUTH_LDAP, AUTH_OID, ADMIN_ROLE, SIGNUP_WITH_NO_AUTH
14
15
  from cornflow.shared.exceptions import (
15
16
  EndpointNotImplemented,
16
17
  InvalidCredentials,
@@ -23,6 +24,8 @@ class SignUpEndpoint(BaseMetaResource):
23
24
  Endpoint used to sign up to the cornflow web server.
24
25
  """
25
26
 
27
+ ROLES_WITH_ACCESS = [ADMIN_ROLE]
28
+
26
29
  def __init__(self):
27
30
  super().__init__()
28
31
  self.data_model = UserModel
@@ -30,6 +33,11 @@ class SignUpEndpoint(BaseMetaResource):
30
33
  self.user_role_association = UserRoleModel
31
34
 
32
35
  @doc(description="Sign up", tags=["Users"])
36
+ @authenticate(
37
+ auth_class=Auth(),
38
+ optional_auth="SIGNUP_ACTIVATED",
39
+ no_auth_list=[SIGNUP_WITH_NO_AUTH],
40
+ )
33
41
  @use_kwargs(SignupRequest, location="json")
34
42
  def post(self, **kwargs):
35
43
  """
@@ -57,14 +65,12 @@ class SignUpEndpoint(BaseMetaResource):
57
65
  if auth_type == AUTH_LDAP:
58
66
  err = "The user has to sign up on the active directory"
59
67
  raise EndpointNotImplemented(
60
- err,
61
- log_txt="Error while user tries to sign up. " + err
68
+ err, log_txt="Error while user tries to sign up. " + err
62
69
  )
63
70
  elif auth_type == AUTH_OID:
64
71
  err = "The user has to sign up with the OpenID protocol"
65
72
  raise EndpointNotImplemented(
66
- err,
67
- log_txt="Error while user tries to sign up. " + err
73
+ err, log_txt="Error while user tries to sign up. " + err
68
74
  )
69
75
 
70
76
  user = self.data_model(kwargs)
@@ -72,14 +78,13 @@ class SignUpEndpoint(BaseMetaResource):
72
78
  if user.check_username_in_use():
73
79
  raise InvalidCredentials(
74
80
  error="Username already in use, please supply another username",
75
- log_txt="Error while user tries to sign up. Username already in use."
76
-
81
+ log_txt="Error while user tries to sign up. Username already in use.",
77
82
  )
78
83
 
79
84
  if user.check_email_in_use():
80
85
  raise InvalidCredentials(
81
86
  error="Email already in use, please supply another email address",
82
- log_txt="Error while user tries to sign up. Email already in use."
87
+ log_txt="Error while user tries to sign up. Email already in use.",
83
88
  )
84
89
 
85
90
  user.save()
@@ -94,8 +99,9 @@ class SignUpEndpoint(BaseMetaResource):
94
99
  token = self.auth_class.generate_token(user.id)
95
100
  except Exception as e:
96
101
  raise InvalidUsage(
97
- error="Error in generating user token: " + str(e), status_code=400,
98
- log_txt="Error while user tries to sign up. Unable to generate token."
102
+ error="Error in generating user token: " + str(e),
103
+ status_code=400,
104
+ log_txt="Error while user tries to sign up. Unable to generate token.",
99
105
  )
100
106
  current_app.logger.info(f"New user created: {user}")
101
107
  return {"token": token, "id": user.id}, 201
@@ -0,0 +1,34 @@
1
+ """
2
+ Rename dag_run_id column to run_id in executions table
3
+
4
+ Revision ID: 999b98e24225
5
+ Revises: 991b98e24225
6
+ Create Date: 2024-04-08 12:00:00.000000
7
+
8
+ """
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision = "abc123456789"
16
+ down_revision = "991b98e24225"
17
+ branch_labels = None
18
+ depends_on = None
19
+
20
+
21
+ def upgrade():
22
+ # ### commands auto generated by Alembic - please adjust! ###
23
+ with op.batch_alter_table("executions", schema=None) as batch_op:
24
+ batch_op.alter_column("dag_run_id", new_column_name="run_id")
25
+
26
+ # ### end Alembic commands ###
27
+
28
+
29
+ def downgrade():
30
+ # ### commands auto generated by Alembic - please adjust! ###
31
+ with op.batch_alter_table("executions", schema=None) as batch_op:
32
+ batch_op.alter_column("run_id", new_column_name="dag_run_id")
33
+
34
+ # ### end Alembic commands ###
@@ -0,0 +1,34 @@
1
+ """empty message
2
+
3
+ Revision ID: cef1df240b27
4
+ Revises: abc123456789
5
+ Create Date: 2025-07-22 19:05:11.146449
6
+
7
+ """
8
+
9
+ from alembic import op
10
+ import sqlalchemy as sa
11
+
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision = 'cef1df240b27'
15
+ down_revision = 'abc123456789'
16
+ branch_labels = None
17
+ depends_on = None
18
+
19
+
20
+ def upgrade():
21
+ # ### commands auto generated by Alembic - please adjust! ###
22
+
23
+ op.rename_table("deployed_dags", "deployed_workflows")
24
+
25
+ # ### end Alembic commands ###
26
+
27
+
28
+ def downgrade():
29
+ # ### commands auto generated by Alembic - please adjust! ###
30
+
31
+ # Rename table back from deployed_workflows to deployed_dags
32
+ op.rename_table("deployed_workflows", "deployed_dags")
33
+
34
+ # ### end Alembic commands ###
@@ -1,10 +1,11 @@
1
1
  """
2
2
  Initialization file for the models module
3
3
  """
4
+
4
5
  from .action import ActionModel
5
6
  from .alarms import AlarmsModel
6
7
  from .case import CaseModel
7
- from .dag import DeployedDAG
8
+ from .dag import DeployedWorkflow
8
9
  from .dag_permissions import PermissionsDAG
9
10
  from .execution import ExecutionModel
10
11
  from .instance import InstanceModel
cornflow/models/dag.py CHANGED
@@ -1,13 +1,12 @@
1
- """
1
+ """ """
2
2
 
3
- """
4
3
  # Import from libraries
5
4
  from cornflow_client.airflow.api import Airflow
6
5
  from cornflow_client.constants import (
7
6
  INSTANCE_SCHEMA,
8
7
  SOLUTION_SCHEMA,
9
8
  INSTANCE_CHECKS_SCHEMA,
10
- SOLUTION_CHECKS_SCHEMA
9
+ SOLUTION_CHECKS_SCHEMA,
11
10
  )
12
11
  from sqlalchemy.dialects.postgresql import TEXT, JSON
13
12
 
@@ -17,12 +16,12 @@ from cornflow.shared import db
17
16
  from cornflow.shared.exceptions import ObjectDoesNotExist
18
17
 
19
18
 
20
- class DeployedDAG(TraceAttributesModel):
19
+ class DeployedWorkflow(TraceAttributesModel):
21
20
  """
22
21
  This model contains the registry of the DAGs that are deployed on the corresponding Airflow server
23
22
  """
24
23
 
25
- __tablename__ = "deployed_dags"
24
+ __tablename__ = "deployed_workflows"
26
25
  id = db.Column(db.String(128), primary_key=True)
27
26
  description = db.Column(TEXT, nullable=True)
28
27
  instance_schema = db.Column(JSON, nullable=True)
@@ -34,8 +33,8 @@ class DeployedDAG(TraceAttributesModel):
34
33
  dag_permissions = db.relationship(
35
34
  "PermissionsDAG",
36
35
  cascade="all,delete",
37
- backref="deployed_dags",
38
- primaryjoin="and_(DeployedDAG.id==PermissionsDAG.dag_id)",
36
+ backref="deployed_workflows",
37
+ primaryjoin="and_(DeployedWorkflow.id==PermissionsDAG.dag_id)",
39
38
  )
40
39
 
41
40
  def __init__(self, data):
@@ -53,14 +52,14 @@ class DeployedDAG(TraceAttributesModel):
53
52
 
54
53
  @staticmethod
55
54
  def get_one_schema(config, dag_name, schema=INSTANCE_SCHEMA):
56
- item = DeployedDAG.get_one_object(dag_name)
55
+ item = DeployedWorkflow.get_one_object(dag_name)
57
56
 
58
57
  if item is None:
59
58
  err = f"The DAG {dag_name} does not exist in the database."
60
59
  raise ObjectDoesNotExist(
61
60
  err,
62
61
  log_txt=f"Error while user tries to register data for DAG {dag_name} "
63
- f"from instance and execution. " + err
62
+ f"from instance and execution. " + err,
64
63
  )
65
64
 
66
65
  if schema == INSTANCE_SCHEMA:
@@ -1,4 +1,4 @@
1
- from cornflow.models.dag import DeployedDAG
1
+ from cornflow.models.dag import DeployedWorkflow
2
2
  from cornflow.models.meta_models import TraceAttributesModel
3
3
  from cornflow.shared import db
4
4
 
@@ -10,7 +10,7 @@ class PermissionsDAG(TraceAttributesModel):
10
10
  id = db.Column(db.Integer, primary_key=True, autoincrement=True)
11
11
 
12
12
  dag_id = db.Column(
13
- db.String(128), db.ForeignKey("deployed_dags.id"), nullable=False
13
+ db.String(128), db.ForeignKey("deployed_workflows.id"), nullable=False
14
14
  )
15
15
  user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
16
16
  user = db.relationship("UserModel", viewonly=True)
@@ -29,7 +29,7 @@ class PermissionsDAG(TraceAttributesModel):
29
29
 
30
30
  @staticmethod
31
31
  def add_all_permissions_to_user(user_id):
32
- dags = DeployedDAG.get_all_objects()
32
+ dags = DeployedWorkflow.get_all_objects()
33
33
  permissions = [
34
34
  PermissionsDAG({"dag_id": dag.id, "user_id": user_id}) for dag in dags
35
35
  ]
@@ -55,7 +55,7 @@ class ExecutionModel(BaseDataModel):
55
55
  db.String(256), db.ForeignKey("instances.id"), nullable=False
56
56
  )
57
57
  config = db.Column(JSON, nullable=False)
58
- dag_run_id = db.Column(db.String(256), nullable=True)
58
+ run_id = db.Column(db.String(256), nullable=True)
59
59
  log_text = db.Column(TEXT, nullable=True)
60
60
  log_json = db.Column(JSON, nullable=True)
61
61
  state = db.Column(db.SmallInteger, default=DEFAULT_EXECUTION_CODE, nullable=False)
@@ -78,8 +78,7 @@ class ExecutionModel(BaseDataModel):
78
78
  + str(self.instance_id)
79
79
  ).encode()
80
80
  ).hexdigest()
81
-
82
- self.dag_run_id = data.get("dag_run_id")
81
+ self.run_id = data.get("run_id")
83
82
  self.state = data.get("state", DEFAULT_EXECUTION_CODE)
84
83
  self.state_message = EXECUTION_STATE_MESSAGE_DICT[self.state]
85
84
  self.config = data.get("config")
@@ -1,6 +1,7 @@
1
1
  """
2
2
  This file contains the PermissionViewRoleModel
3
3
  """
4
+
4
5
  # Imports from internal modules
5
6
  from cornflow.models.meta_models import TraceAttributesModel
6
7
  from cornflow.shared import db
cornflow/models/user.py CHANGED
@@ -5,7 +5,7 @@ This file contains the UserModel
5
5
  # Imports from external libraries
6
6
  import random
7
7
  import string
8
- from datetime import datetime, timezone, timedelta
8
+ from datetime import datetime, timezone
9
9
 
10
10
  # Imports from internal modules
11
11
  from cornflow.models.meta_models import TraceAttributesModel
@@ -43,7 +43,7 @@ class ExecutionSchema(Schema):
43
43
  name = fields.Str()
44
44
  description = fields.Str()
45
45
  schema = fields.Str(required=False)
46
- dag_run_id = fields.Str(required=False, dump_only=True)
46
+ run_id = fields.Str(required=False, dump_only=True)
47
47
  config = fields.Nested(ConfigSchema, required=True)
48
48
  data = fields.Raw(dump_only=True)
49
49
  checks = fields.Raw(required=False, allow_none=True)
@@ -78,6 +78,7 @@ class ExecutionEditRequest(Schema):
78
78
  name = fields.Str()
79
79
  description = fields.Str()
80
80
  data = fields.Raw()
81
+ instance_id = fields.Str(required=False)
81
82
 
82
83
 
83
84
  class ExecutionDagRequest(Schema):
@@ -119,7 +120,19 @@ class ExecutionDetailsEndpointWithIndicatorsResponse(ExecutionDetailsEndpointRes
119
120
  return obj.user.username
120
121
  return None
121
122
 
123
+ def get_first_name(self, obj):
124
+ if hasattr(obj, "user") and obj.user is not None:
125
+ return obj.user.first_name
126
+ return None
127
+
128
+ def get_last_name(self, obj):
129
+ if hasattr(obj, "user") and obj.user is not None:
130
+ return obj.user.last_name
131
+ return None
132
+
122
133
  username = fields.Method("get_username")
134
+ first_name = fields.Method("get_first_name")
135
+ last_name = fields.Method("get_last_name")
123
136
 
124
137
 
125
138
  class ExecutionDetailsWithIndicatorsAndLogResponse(
@@ -4,5 +4,5 @@ from marshmallow import fields, Schema
4
4
  class HealthResponse(Schema):
5
5
 
6
6
  cornflow_status = fields.Str()
7
- airflow_status = fields.Str()
7
+ backend_status = fields.Str()
8
8
  cornflow_version = fields.Str()
@@ -25,6 +25,7 @@ from cornflow.shared.const import (
25
25
  AUTH_OID,
26
26
  PERMISSION_METHOD_MAP,
27
27
  INTERNAL_TOKEN_ISSUER,
28
+ OID_PROVIDER_AZURE,
28
29
  )
29
30
  from cornflow.shared.exceptions import (
30
31
  CommunicationError,
@@ -300,7 +301,19 @@ class Auth:
300
301
  :rtype: dict
301
302
  """
302
303
  # Fetch keys from provider
303
- jwks_url = f"{provider_url.rstrip('/')}/.well-known/jwks.json"
304
+ # For Azure AD, we need to use the discovery endpoint to get the jwks_uri
305
+ oid_provider = current_app.config["OID_PROVIDER"]
306
+ if oid_provider == OID_PROVIDER_AZURE:
307
+ tenant_id = provider_url.split("/")[
308
+ 3
309
+ ] # Extract tenant ID from provider URL
310
+ jwks_url = (
311
+ f"https://login.microsoftonline.com/{tenant_id}/discovery/v2.0/keys"
312
+ )
313
+ else:
314
+ # For other providers, use the standard well-known endpoint
315
+ jwks_url = f"{provider_url.rstrip('/')}/.well-known/jwks.json"
316
+
304
317
  try:
305
318
  response = requests.get(jwks_url)
306
319
  response.raise_for_status()
@@ -1,17 +1,23 @@
1
1
  """
2
2
  This file contains the decorator used for the authentication
3
3
  """
4
+
4
5
  from functools import wraps
5
6
  from .auth import Auth
6
7
  from cornflow.shared.exceptions import InvalidCredentials
8
+ from flask import current_app
7
9
 
8
10
 
9
- def authenticate(auth_class: Auth):
11
+ def authenticate(
12
+ auth_class: Auth, optional_auth: str = None, no_auth_list: list = None
13
+ ):
10
14
  """
11
15
  This is the decorator used for the authentication
12
16
 
13
17
  :param auth_class: the class used for the authentication. It should be `BaseAuth` or a class that inherits from it
14
18
  and that has a authenticate method.
19
+ :param optional_auth: the env variable that indicates if authentication should be deactivated
20
+ :param no_auth_list: the list of the values that indicates if authentication should be deactivated
15
21
  :type auth_class: `BaseAuth`
16
22
  :return: the wrapped function
17
23
  """
@@ -32,11 +38,35 @@ def authenticate(auth_class: Auth):
32
38
  :param kwargs: the original kwargs sent to the decorated function
33
39
  :return: the result of the call to the function
34
40
  """
41
+ if endpoint_qualified_for_no_auth(no_auth_list, optional_auth):
42
+ # Authentication is deactivated for this value
43
+ return func(*args, **kwargs)
44
+
35
45
  if auth_class.authenticate():
36
46
  return func(*args, **kwargs)
37
47
  else:
38
48
  raise InvalidCredentials("Unable to authenticate the user")
39
-
40
49
  return wrapper
41
50
 
42
51
  return decorator
52
+
53
+ def endpoint_qualified_for_no_auth(no_auth_list, optional_auth):
54
+ """
55
+ This function is used to check if the endpoint is qualified for no authentication.
56
+
57
+ :param no_auth_list: the list of the values that indicates if authentication should be deactivated
58
+ :param optional_auth: the env variable that indicates if authentication should be deactivated
59
+ :type no_auth_list: list
60
+ :type optional_auth: str
61
+ :return: True if the endpoint is qualified for no authentication, False otherwise
62
+ """
63
+ if optional_auth is None:
64
+ return False
65
+ if no_auth_list is None:
66
+ raise InvalidCredentials(
67
+ "The list of the values that indicates if authentication should be deactivated is not defined"
68
+ )
69
+ env_variable = current_app.config[optional_auth]
70
+ if env_variable in no_auth_list:
71
+ return True
72
+ return False
cornflow/shared/const.py CHANGED
@@ -1,7 +1,13 @@
1
1
  """
2
2
  In this file we import the values for different constants on cornflow server
3
3
  """
4
- CORNFLOW_VERSION = "1.2.3.a5"
4
+
5
+ # CORNFLOW BACKEND
6
+ AIRFLOW_BACKEND = 1
7
+ DATABRICKS_BACKEND = 2
8
+
9
+
10
+ CORNFLOW_VERSION = "1.3.0rc1"
5
11
  INTERNAL_TOKEN_ISSUER = "cornflow"
6
12
 
7
13
  # endpoints responses for health check
@@ -44,7 +50,32 @@ AIRFLOW_TO_STATE_MAP = dict(
44
50
  failed=EXEC_STATE_ERROR,
45
51
  queued=EXEC_STATE_QUEUED,
46
52
  )
53
+ # SIGNUP OPTIONS
54
+ # NO_SIGNUP: no signup endpoint
55
+ # SIGNUP_WITH_NO_AUTH: signup endpoint with no auth
56
+ # SIGNUP_WITH_AUTH: signup endpoint with auth
57
+ NO_SIGNUP = 0
58
+ SIGNUP_WITH_NO_AUTH = 1
59
+ SIGNUP_WITH_AUTH = 2
60
+
61
+ DATABRICKS_TO_STATE_MAP = dict(
62
+ BLOCKED=EXEC_STATE_QUEUED,
63
+ PENDING=EXEC_STATE_QUEUED,
64
+ QUEUED=EXEC_STATE_QUEUED,
65
+ RUNNING=EXEC_STATE_RUNNING,
66
+ TERMINATING=EXEC_STATE_RUNNING,
67
+ SUCCESS=EXEC_STATE_CORRECT,
68
+ USER_CANCELED=EXEC_STATE_STOPPED,
69
+ OTHER_FINISH_ERROR=EXEC_STATE_ERROR,
70
+ RUN_EXECUTION_ERROR=EXEC_STATE_ERROR,
71
+ )
72
+
73
+ DATABRICKS_FINISH_TO_STATE_MAP = dict(
74
+ SUCCESS=EXEC_STATE_CORRECT,
75
+ USER_CANCELED=EXEC_STATE_STOPPED,
76
+ )
47
77
 
78
+ DATABRICKS_TERMINATE_STATE = "TERMINATED"
48
79
  # These codes and names are inherited from flask app builder in order to have the same names and values
49
80
  # as this library that is the base of airflow
50
81
  AUTH_DB = 1
@@ -52,6 +83,11 @@ AUTH_LDAP = 2
52
83
  AUTH_OAUTH = 4
53
84
  AUTH_OID = 0
54
85
 
86
+ # OID possible providers
87
+ OID_PROVIDER_AWS = 0
88
+ OID_PROVIDER_AZURE = 1
89
+ OID_OTHER = 2
90
+
55
91
  GET_ACTION = 1
56
92
  PATCH_ACTION = 2
57
93
  POST_ACTION = 3
@@ -121,3 +157,24 @@ AIRFLOW_NOT_REACHABLE_MSG = "Airflow is not reachable"
121
157
  DAG_PAUSED_MSG = "The dag exists but it is paused in airflow"
122
158
  AIRFLOW_ERROR_MSG = "Airflow responded with an error:"
123
159
  DATA_DOES_NOT_EXIST_MSG = "The data entity does not exist on the database"
160
+
161
+ # Conditional endpoints
162
+ CONDITIONAL_ENDPOINTS = {
163
+ "signup": "/signup/",
164
+ "login": "/login/",
165
+ }
166
+
167
+
168
+ # Orchestrator constants
169
+ config_orchestrator = {
170
+ "airflow": {
171
+ "name": "Airflow",
172
+ "def_schema": "solve_model_dag",
173
+ "run_id": "dag_run_id",
174
+ },
175
+ "databricks": {
176
+ "name": "Databricks",
177
+ "def_schema": "979073949072767",
178
+ "run_id": "run_id",
179
+ },
180
+ }
@@ -5,7 +5,7 @@ on a flask REST API server
5
5
 
6
6
  from flask import jsonify
7
7
  from webargs.flaskparser import parser
8
- from cornflow_client.constants import AirflowError
8
+ from cornflow_client.constants import AirflowError, DatabricksError
9
9
  from werkzeug.exceptions import HTTPException
10
10
  import traceback
11
11
 
@@ -148,6 +148,7 @@ def initialize_errorhandlers(app):
148
148
  @app.errorhandler(InvalidCredentials)
149
149
  @app.errorhandler(EndpointNotImplemented)
150
150
  @app.errorhandler(AirflowError)
151
+ @app.errorhandler(DatabricksError)
151
152
  @app.errorhandler(InvalidData)
152
153
  @app.errorhandler(InvalidPatch)
153
154
  @app.errorhandler(ConfigurationError)