cornflow 1.2.3a2__py3-none-any.whl → 1.2.3a4__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.
@@ -0,0 +1,539 @@
1
+ import unittest
2
+ import os
3
+ import json
4
+ from unittest.mock import patch, MagicMock
5
+ from cornflow.shared import db
6
+ from cornflow.tests.custom_test_case import CustomTestCase
7
+ from cornflow.models import ViewModel
8
+ from cornflow.commands.access import access_init_command
9
+ from cornflow.shared.const import (
10
+ VIEWER_ROLE,
11
+ PLANNER_ROLE,
12
+ POST_ACTION,
13
+ PATCH_ACTION,
14
+ DELETE_ACTION,
15
+ GET_ACTION,
16
+ )
17
+
18
+
19
+ class ExternalRoleCreationTestCase(CustomTestCase):
20
+ """
21
+ Test cases for external app custom role creation and removal functionality
22
+ """
23
+
24
+ def _load_expected_permissions(self, test_name):
25
+ """Helper method to load expected permissions from JSON file"""
26
+ test_data_path = os.path.join(
27
+ os.path.dirname(__file__), "..", "data", "expected_permissions.json"
28
+ )
29
+ with open(test_data_path, "r") as f:
30
+ data = json.load(f)
31
+ return [
32
+ (role_id, action_id, endpoint_name)
33
+ for role_id, action_id, endpoint_name in data[test_name]
34
+ ]
35
+
36
+ def _create_mock_external_app_resources(self):
37
+ """Helper method to create mock external app resources"""
38
+ # Create mock endpoint classes for external app
39
+ mock_production_endpoint = MagicMock()
40
+ mock_production_endpoint.ROLES_WITH_ACCESS = [
41
+ 888,
42
+ PLANNER_ROLE,
43
+ ] # Custom role + standard role
44
+ mock_production_endpoint.DESCRIPTION = "Production planning endpoint"
45
+
46
+ mock_quality_endpoint = MagicMock()
47
+ mock_quality_endpoint.ROLES_WITH_ACCESS = [
48
+ 777,
49
+ VIEWER_ROLE,
50
+ ] # Custom role + standard role
51
+ mock_quality_endpoint.DESCRIPTION = "Quality control endpoint"
52
+
53
+ mock_scheduling_endpoint = MagicMock()
54
+ mock_scheduling_endpoint.ROLES_WITH_ACCESS = [
55
+ 888,
56
+ 777,
57
+ PLANNER_ROLE,
58
+ ] # Multiple custom roles
59
+ mock_scheduling_endpoint.DESCRIPTION = "Scheduling optimizer endpoint"
60
+
61
+ # Create mock resources structure for external app endpoints
62
+ mock_resources = [
63
+ {
64
+ "endpoint": "production_planning", # External app endpoint
65
+ "urls": "/production-planning/",
66
+ "resource": mock_production_endpoint,
67
+ },
68
+ {
69
+ "endpoint": "quality_control", # External app endpoint
70
+ "urls": "/quality-control/",
71
+ "resource": mock_quality_endpoint,
72
+ },
73
+ {
74
+ "endpoint": "scheduling_optimizer", # External app endpoint
75
+ "urls": "/scheduling/",
76
+ "resource": mock_scheduling_endpoint,
77
+ },
78
+ ]
79
+
80
+ return mock_resources
81
+
82
+ @patch("cornflow.commands.auxiliar.import_module")
83
+ @patch("cornflow.commands.views.import_module")
84
+ @patch.dict(
85
+ os.environ, {"EXTERNAL_APP": "1", "EXTERNAL_APP_MODULE": "external_test_app"}
86
+ )
87
+ def test_custom_role_creation_removal(
88
+ self, mock_import_views, mock_import_auxiliar
89
+ ):
90
+ """
91
+ Test that custom roles (like role 888) are properly created and removed
92
+ when external app is configured with EXTRA_PERMISSION_ASSIGNATION
93
+ """
94
+ # Mock external app configuration
95
+ mock_external_app = MagicMock()
96
+
97
+ # Mock the shared.const module
98
+ mock_shared = MagicMock()
99
+ mock_const = MagicMock()
100
+ mock_const.EXTRA_PERMISSION_ASSIGNATION = [
101
+ # Try adding an existing permission and it works
102
+ (888, GET_ACTION, "production_planning"),
103
+ # Try adding additional permission
104
+ (888, POST_ACTION, "production_planning"),
105
+ (777, PATCH_ACTION, "quality_control"),
106
+ (VIEWER_ROLE, POST_ACTION, "scheduling_optimizer"),
107
+ (PLANNER_ROLE, DELETE_ACTION, "quality_control"),
108
+ ]
109
+ mock_shared.const = mock_const
110
+ mock_external_app.shared = mock_shared
111
+
112
+ # Mock the endpoints.resources with fake external app endpoints
113
+ mock_endpoints = MagicMock()
114
+ mock_endpoints.resources = self._create_mock_external_app_resources()
115
+ mock_external_app.endpoints = mock_endpoints
116
+
117
+ mock_import_views.return_value = mock_external_app
118
+ mock_import_auxiliar.return_value = mock_external_app
119
+
120
+ # Run the permissions registration
121
+ from cornflow.commands.access import access_init_command
122
+
123
+ # Mock the database session for testing
124
+ with patch.object(db.session, "commit"):
125
+ with patch.object(db.session, "rollback"):
126
+ # Run the complete access initialization
127
+ access_init_command(verbose=True)
128
+
129
+ # Verify that custom permissions were created for the external roles
130
+ from cornflow.models import PermissionViewRoleModel
131
+
132
+ # Get all permissions for external app endpoints
133
+ all_permissions = PermissionViewRoleModel.query.all()
134
+ external_permissions = [
135
+ perm
136
+ for perm in all_permissions
137
+ if perm.api_view.name
138
+ in [
139
+ "production_planning",
140
+ "quality_control",
141
+ "scheduling_optimizer",
142
+ ]
143
+ ]
144
+
145
+ # Load expected permissions from JSON file
146
+ expected_permissions = self._load_expected_permissions(
147
+ "test_custom_role_creation_removal"
148
+ )
149
+
150
+ # Verify each expected permission exists
151
+ for role_id, action_id, endpoint_name in expected_permissions:
152
+ permission_exists = any(
153
+ p.role_id == role_id
154
+ and p.action_id == action_id
155
+ and p.api_view.name == endpoint_name
156
+ for p in external_permissions
157
+ )
158
+ self.assertTrue(
159
+ permission_exists,
160
+ f"Expected permission not found: role_id={role_id}, action_id={action_id}, endpoint={endpoint_name}",
161
+ )
162
+
163
+ # Verify we don't have unexpected permissions
164
+ actual_permission_tuples = {
165
+ (p.role_id, p.action_id, p.api_view.name)
166
+ for p in external_permissions
167
+ }
168
+ expected_permission_tuples = set(expected_permissions)
169
+
170
+ unexpected_permissions = (
171
+ actual_permission_tuples - expected_permission_tuples
172
+ )
173
+ self.assertEqual(
174
+ len(unexpected_permissions),
175
+ 0,
176
+ f"Found unexpected permissions: {unexpected_permissions}",
177
+ )
178
+
179
+ missing_permissions = (
180
+ expected_permission_tuples - actual_permission_tuples
181
+ )
182
+ self.assertEqual(
183
+ len(missing_permissions),
184
+ 0,
185
+ f"Missing expected permissions: {missing_permissions}",
186
+ )
187
+
188
+ @patch("cornflow.commands.auxiliar.import_module")
189
+ @patch("cornflow.commands.views.import_module")
190
+ @patch.dict(
191
+ os.environ, {"EXTERNAL_APP": "1", "EXTERNAL_APP_MODULE": "external_test_app"}
192
+ )
193
+ def test_role_removal_when_not_in_config(
194
+ self, mock_import_views, mock_import_auxiliar
195
+ ):
196
+ """
197
+ Test that roles are properly removed when they're no longer in EXTRA_PERMISSION_ASSIGNATION
198
+ """
199
+ # First, create roles with permissions
200
+ mock_external_app = MagicMock()
201
+
202
+ # Mock the shared.const module
203
+ mock_shared = MagicMock()
204
+ mock_const = MagicMock()
205
+ mock_const.EXTRA_PERMISSION_ASSIGNATION = [
206
+ (10000, POST_ACTION, "production_planning"),
207
+ (999, PATCH_ACTION, "quality_control"),
208
+ ]
209
+ mock_shared.const = mock_const
210
+ mock_external_app.shared = mock_shared
211
+
212
+ # Mock the endpoints.resources
213
+ mock_endpoints = MagicMock()
214
+ mock_endpoints.resources = self._create_mock_external_app_resources()
215
+ mock_external_app.endpoints = mock_endpoints
216
+
217
+ mock_import_views.return_value = mock_external_app
218
+ mock_import_auxiliar.return_value = mock_external_app
219
+
220
+ # Create initial roles
221
+ access_init_command(verbose=True)
222
+
223
+ # Now update config to remove role 777
224
+ mock_const.EXTRA_PERMISSION_ASSIGNATION = [
225
+ (10000, POST_ACTION, "production_planning"),
226
+ ]
227
+
228
+ # Re-run permissions registration
229
+ access_init_command(verbose=True)
230
+
231
+ # Verify role 888 still has permissions but 777 does not
232
+ from cornflow.models import PermissionViewRoleModel
233
+
234
+ permissions_10000 = PermissionViewRoleModel.query.filter_by(role_id=10000).all()
235
+ permissions_999 = PermissionViewRoleModel.query.filter_by(role_id=999).all()
236
+
237
+ self.assertTrue(len(permissions_10000) > 0)
238
+ self.assertEqual(len(permissions_999), 0)
239
+
240
+ def test_fallback_when_no_external_config(self):
241
+ """
242
+ Test that the system falls back gracefully when EXTRA_PERMISSION_ASSIGNATION is not available
243
+ """
244
+ # Should not raise any exceptions
245
+ try:
246
+ access_init_command(verbose=True)
247
+ except Exception as e:
248
+ self.fail(f"access_init_command raised an exception: {e}")
249
+
250
+ @patch("cornflow.commands.auxiliar.import_module")
251
+ @patch("cornflow.commands.views.import_module")
252
+ @patch.dict(
253
+ os.environ, {"EXTERNAL_APP": "1", "EXTERNAL_APP_MODULE": "external_test_app"}
254
+ )
255
+ def test_external_app_missing_extra_permissions(
256
+ self, mock_import_views, mock_import_auxiliar
257
+ ):
258
+ """
259
+ Test graceful handling when external app doesn't have EXTRA_PERMISSION_ASSIGNATION
260
+ """
261
+ # Mock external app without EXTRA_PERMISSION_ASSIGNATION
262
+ mock_external_app = MagicMock()
263
+
264
+ # Mock the shared module but without const.EXTRA_PERMISSION_ASSIGNATION
265
+ mock_shared = MagicMock()
266
+ mock_const = MagicMock()
267
+ # Don't set EXTRA_PERMISSION_ASSIGNATION to trigger AttributeError
268
+ del mock_const.EXTRA_PERMISSION_ASSIGNATION
269
+ mock_shared.const = mock_const
270
+ mock_external_app.shared = mock_shared
271
+
272
+ # Mock the endpoints.resources
273
+ mock_endpoints = MagicMock()
274
+ mock_endpoints.resources = self._create_mock_external_app_resources()
275
+ mock_external_app.endpoints = mock_endpoints
276
+
277
+ mock_import_views.return_value = mock_external_app
278
+ mock_import_auxiliar.return_value = mock_external_app
279
+
280
+ # Should not raise any exceptions, should fall back gracefully
281
+ try:
282
+ access_init_command(verbose=True)
283
+ except Exception as e:
284
+ self.fail(f"access_init_command raised an exception: {e}")
285
+
286
+ @patch("cornflow.commands.auxiliar.import_module")
287
+ @patch("cornflow.commands.views.import_module")
288
+ @patch.dict(
289
+ os.environ, {"EXTERNAL_APP": "1", "EXTERNAL_APP_MODULE": "external_test_app"}
290
+ )
291
+ def test_standard_role_extended_permissions(
292
+ self, mock_import_views, mock_import_auxiliar
293
+ ):
294
+ """
295
+ Test that standard roles (like VIEWER_ROLE) can get extended permissions from external app
296
+ """
297
+ # Mock external app configuration with extended permissions for existing roles
298
+ mock_external_app = MagicMock()
299
+
300
+ # Mock the shared.const module
301
+ mock_shared = MagicMock()
302
+ mock_const = MagicMock()
303
+ mock_const.EXTRA_PERMISSION_ASSIGNATION = [
304
+ (VIEWER_ROLE, POST_ACTION, "production_planning"), # Extend standard role
305
+ (PLANNER_ROLE, DELETE_ACTION, "quality_control"), # Extend standard role
306
+ ]
307
+ mock_shared.const = mock_const
308
+ mock_external_app.shared = mock_shared
309
+
310
+ # Mock the endpoints.resources
311
+ mock_endpoints = MagicMock()
312
+ mock_endpoints.resources = self._create_mock_external_app_resources()
313
+ mock_external_app.endpoints = mock_endpoints
314
+
315
+ mock_import_views.return_value = mock_external_app
316
+ mock_import_auxiliar.return_value = mock_external_app
317
+
318
+ # Mock the database session for testing
319
+ with patch.object(db.session, "commit"):
320
+ with patch.object(db.session, "rollback"):
321
+ # Run the complete access initialization
322
+ access_init_command(verbose=True)
323
+
324
+ # Verify that existing roles got the additional permissions
325
+ from cornflow.models import PermissionViewRoleModel
326
+
327
+ # Check that VIEWER_ROLE has additional permissions
328
+ viewer_permissions = PermissionViewRoleModel.query.filter_by(
329
+ role_id=VIEWER_ROLE
330
+ ).all()
331
+ self.assertTrue(len(viewer_permissions) > 0)
332
+
333
+ # Check that PLANNER_ROLE has additional permissions
334
+ planner_permissions = PermissionViewRoleModel.query.filter_by(
335
+ role_id=PLANNER_ROLE
336
+ ).all()
337
+ self.assertTrue(len(planner_permissions) > 0)
338
+
339
+ @patch("cornflow.commands.auxiliar.import_module")
340
+ @patch("cornflow.commands.views.import_module")
341
+ @patch.dict(
342
+ os.environ, {"EXTERNAL_APP": "1", "EXTERNAL_APP_MODULE": "external_test_app"}
343
+ )
344
+ def test_view_update_and_deletion(self, mock_import_views, mock_import_auxiliar):
345
+ """
346
+ Test that views are updated when URLs change and deleted when resources are removed
347
+ """
348
+ # === INITIAL SETUP ===
349
+ # Create initial mock external app with 3 endpoints
350
+ mock_external_app_initial = MagicMock()
351
+
352
+ # Initial mock endpoints
353
+ mock_production_endpoint = MagicMock()
354
+ mock_production_endpoint.ROLES_WITH_ACCESS = [888]
355
+ mock_production_endpoint.DESCRIPTION = "Production planning endpoint"
356
+
357
+ mock_quality_endpoint = MagicMock()
358
+ mock_quality_endpoint.ROLES_WITH_ACCESS = [777]
359
+ mock_quality_endpoint.DESCRIPTION = "Quality control endpoint"
360
+
361
+ mock_scheduling_endpoint = MagicMock()
362
+ mock_scheduling_endpoint.ROLES_WITH_ACCESS = [888, 777]
363
+ mock_scheduling_endpoint.DESCRIPTION = "Scheduling optimizer endpoint"
364
+
365
+ # Initial resources structure
366
+ initial_resources = [
367
+ {
368
+ "endpoint": "production_planning",
369
+ "urls": "/production-planning/",
370
+ "resource": mock_production_endpoint,
371
+ },
372
+ {
373
+ "endpoint": "quality_control",
374
+ "urls": "/quality-control/",
375
+ "resource": mock_quality_endpoint,
376
+ },
377
+ {
378
+ "endpoint": "scheduling_optimizer",
379
+ "urls": "/scheduling/",
380
+ "resource": mock_scheduling_endpoint,
381
+ },
382
+ ]
383
+
384
+ # Mock the shared.const module (no extra permissions)
385
+ mock_shared_initial = MagicMock()
386
+ mock_const_initial = MagicMock()
387
+ mock_const_initial.EXTRA_PERMISSION_ASSIGNATION = []
388
+ mock_shared_initial.const = mock_const_initial
389
+ mock_external_app_initial.shared = mock_shared_initial
390
+
391
+ # Mock the endpoints.resources
392
+ mock_endpoints_initial = MagicMock()
393
+ mock_endpoints_initial.resources = initial_resources
394
+ mock_external_app_initial.endpoints = mock_endpoints_initial
395
+
396
+ mock_import_views.return_value = mock_external_app_initial
397
+ mock_import_auxiliar.return_value = mock_external_app_initial
398
+
399
+ # === INITIAL ACCESS INIT ===
400
+
401
+ # Mock the database session for testing - INITIAL SETUP ONLY
402
+ with patch.object(db.session, "commit"):
403
+ with patch.object(db.session, "rollback"):
404
+ # Run initial access initialization
405
+ access_init_command(verbose=True)
406
+
407
+ # Verify initial views were created
408
+ initial_views = ViewModel.query.filter(
409
+ ViewModel.name.in_(
410
+ [
411
+ "production_planning",
412
+ "quality_control",
413
+ "scheduling_optimizer",
414
+ ]
415
+ )
416
+ ).all()
417
+
418
+ self.assertEqual(
419
+ len(initial_views), 3, "Expected 3 initial views to be created"
420
+ )
421
+
422
+ # Get initial URLs
423
+ initial_views_dict = {
424
+ view.name: view.url_rule for view in initial_views
425
+ }
426
+ self.assertEqual(
427
+ initial_views_dict["production_planning"], "/production-planning/"
428
+ )
429
+ self.assertEqual(
430
+ initial_views_dict["quality_control"], "/quality-control/"
431
+ )
432
+ self.assertEqual(
433
+ initial_views_dict["scheduling_optimizer"], "/scheduling/"
434
+ )
435
+
436
+ # === MODIFY CONFIGURATION ===
437
+ # Create updated mock external app with:
438
+ # 1. Changed URL for production_planning
439
+ # 2. Changed URL for quality_control
440
+ # 3. Remove scheduling_optimizer entirely
441
+ mock_external_app_updated = MagicMock()
442
+
443
+ # Updated resources structure (scheduling_optimizer removed, URLs changed)
444
+ updated_resources = [
445
+ {
446
+ "endpoint": "production_planning",
447
+ "urls": "/new-production-planning/", # CHANGED URL
448
+ "resource": mock_production_endpoint,
449
+ },
450
+ {
451
+ "endpoint": "quality_control",
452
+ "urls": "/quality-control/",
453
+ "resource": mock_quality_endpoint,
454
+ },
455
+ # scheduling_optimizer REMOVED entirely
456
+ ]
457
+
458
+ # Mock shared const (still no extra permissions)
459
+ mock_shared_updated = MagicMock()
460
+ mock_const_updated = MagicMock()
461
+ mock_const_updated.EXTRA_PERMISSION_ASSIGNATION = [] # Empty list
462
+ mock_shared_updated.const = mock_const_updated
463
+ mock_external_app_updated.shared = mock_shared_updated
464
+
465
+ # Mock updated endpoints.resources
466
+ mock_endpoints_updated = MagicMock()
467
+ mock_endpoints_updated.resources = updated_resources
468
+ mock_external_app_updated.endpoints = mock_endpoints_updated
469
+
470
+ # Update mocks to return updated configuration
471
+ mock_import_views.return_value = mock_external_app_updated
472
+ mock_import_auxiliar.return_value = mock_external_app_updated
473
+
474
+ # === SECOND ACCESS INIT (with updated config) ===
475
+ # Allow real commits for the update to be persisted
476
+ access_init_command(verbose=True)
477
+
478
+ # === VERIFY UPDATES ===
479
+ # Check that URLs were updated
480
+ updated_views = ViewModel.query.filter(
481
+ ViewModel.name.in_(["production_planning", "quality_control"])
482
+ ).all()
483
+
484
+ self.assertEqual(
485
+ len(updated_views), 2, "Expected 2 views to remain after update"
486
+ )
487
+
488
+ updated_views_dict = {view.name: view.url_rule for view in updated_views}
489
+ self.assertEqual(
490
+ updated_views_dict["production_planning"],
491
+ "/new-production-planning/",
492
+ "production_planning URL should be updated",
493
+ )
494
+ self.assertEqual(
495
+ updated_views_dict["quality_control"],
496
+ "/quality-control/",
497
+ "quality_control URL should be updated",
498
+ )
499
+
500
+ # === VERIFY DELETION ===
501
+ # Check that scheduling_optimizer view was deleted
502
+ deleted_view = ViewModel.query.filter_by(name="scheduling_optimizer").first()
503
+ self.assertIsNone(deleted_view, "scheduling_optimizer view should be deleted")
504
+
505
+ # === VERIFY PERMISSIONS ARE CLEANED UP ===
506
+ from cornflow.models import PermissionViewRoleModel
507
+
508
+ # Check that permissions for deleted view are cleaned up
509
+ remaining_permissions = PermissionViewRoleModel.query.all()
510
+ scheduling_permissions = [
511
+ perm
512
+ for perm in remaining_permissions
513
+ if perm.api_view and perm.api_view.name == "scheduling_optimizer"
514
+ ]
515
+ self.assertEqual(
516
+ len(scheduling_permissions),
517
+ 0,
518
+ "All permissions for deleted scheduling_optimizer view should be removed",
519
+ )
520
+
521
+ # Check that permissions for remaining views still exist
522
+ remaining_external_permissions = [
523
+ perm
524
+ for perm in remaining_permissions
525
+ if perm.api_view
526
+ and perm.api_view.name in ["production_planning", "quality_control"]
527
+ ]
528
+
529
+ all_view_names = [
530
+ perm.api_view.name for perm in remaining_permissions if perm.api_view
531
+ ]
532
+ self.assertTrue(
533
+ len(remaining_external_permissions) > 0,
534
+ f"Permissions for remaining views should still exist. Found views: {set(all_view_names)}",
535
+ )
536
+
537
+
538
+ if __name__ == "__main__":
539
+ unittest.main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cornflow
3
- Version: 1.2.3a2
3
+ Version: 1.2.3a4
4
4
  Summary: cornflow is an open source multi-solver optimization server with a REST API built using flask.
5
5
  Home-page: https://github.com/baobabsoluciones/cornflow
6
6
  Author: baobab soluciones
@@ -14,7 +14,7 @@ Requires-Dist: alembic==1.9.2
14
14
  Requires-Dist: apispec<=6.3.0
15
15
  Requires-Dist: cachetools==5.3.3
16
16
  Requires-Dist: click<=8.1.7
17
- Requires-Dist: cornflow-client>=1.2.0
17
+ Requires-Dist: cornflow-client>=1.2.3.a4
18
18
  Requires-Dist: cryptography<=44.0.1
19
19
  Requires-Dist: disposable-email-domains>=0.0.86
20
20
  Requires-Dist: Flask==2.3.2
@@ -5,7 +5,7 @@ airflow_config/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
5
5
  airflow_config/plugins/XCom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  airflow_config/plugins/XCom/gce_xcom_backend.py,sha256=vCGvF2jbfZt5bOv-pk5Q_kUR6LomFUojIymimSJmj3o,1795
7
7
  cornflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- cornflow/app.py,sha256=SHHShEmTqvzZtNShn5AInJxSvwnUmgILwUNpzxNtlNs,7690
8
+ cornflow/app.py,sha256=5ebZuYTfUFKVQ-lxcTeS0hATPGn3zacYdQ7jkApwmrE,7698
9
9
  cornflow/config.py,sha256=c3CNu6wzm5mDJLF6GjrnBKQSNKsRr69S2vQ9jCSAhYw,5628
10
10
  cornflow/gunicorn.py,sha256=uO-Yk7w7nvQSWh12iDxsVvlG-_2BiKIIjm2UiTk4P9E,480
11
11
  cornflow/cli/__init__.py,sha256=2QfFLxLcX5zYt3Ok3QKNWQvUvAeEnLsH7xiGN3GjwFU,853
@@ -14,9 +14,9 @@ cornflow/cli/arguments.py,sha256=9EEyyny5cJJ1t3WAs6zMgTDvTre0JdQ2N_oZfFQmixs,657
14
14
  cornflow/cli/config.py,sha256=AWWoLj3AIbKISIJ58Q89OdC_Ocjs_TPLCMF2jTEb5Sw,1258
15
15
  cornflow/cli/migrations.py,sha256=nJbmulqwFxuaSPmo9hJiTl1pUSojeXfAdd9aGM_g9ic,2426
16
16
  cornflow/cli/permissions.py,sha256=fyIp5M0ZpJWVf_tGZG0DN0Gnk1geruadkdKK11cmRk8,1116
17
- cornflow/cli/roles.py,sha256=o7P3yt1g2us2B-vv5R7sXNDgUagPsvPGc7ABL0lCBOQ,521
17
+ cornflow/cli/roles.py,sha256=1dvAq1Bp1iEAUUfXFeL8kYjI0ZchYlDwIBQpTtwV6vs,529
18
18
  cornflow/cli/schemas.py,sha256=s9IUJWa2G0kpqJaN6PcwbwZtGChTaqq451QqWEyWPBI,6197
19
- cornflow/cli/service.py,sha256=YGp30zEdrCZg2IIonA0aF2nh2d_0-Q_Cv17HjUQGkXY,12412
19
+ cornflow/cli/service.py,sha256=iAV-RvKpCoUg2Nr4BqatTD6he_2THWuhQdJrUoFlqaI,13303
20
20
  cornflow/cli/users.py,sha256=VBUqOrS80qdp9E4XLn4ihocHVNWVhWSt19tp0o8mZII,2324
21
21
  cornflow/cli/utils.py,sha256=p54xJEnYWda6rqSQDoZU2qZrFu9kTs4FoF0y3pJLQvI,1377
22
22
  cornflow/cli/views.py,sha256=BjJRIQ5T3C8zl3-pVVkToVjQ6xdAGwnuT9LVEnn_jqM,746
@@ -28,15 +28,16 @@ cornflow/cli/tools/schema_generator.py,sha256=BQxfB4RvA010BjrXZ4D33AvWU9e7jfZ_m2
28
28
  cornflow/cli/tools/schemas_tools.py,sha256=cTAM4_-0uu7dNlv95Oo2edM5qWEnfNKzIt_EhP6qx4c,2232
29
29
  cornflow/cli/tools/tools.py,sha256=Qm0X-wHN12vXYJNRHONGjqDZewwXyXy4R_j4UT_XMLs,929
30
30
  cornflow/commands/__init__.py,sha256=_7mi2Sd8bnaSujU-L78z8Zrswz68NJ2xoocYpsEYmPM,544
31
- cornflow/commands/access.py,sha256=NTZJFF9la8TDuMcD_ISQtJTj-wtM2p1dddokQJHtkj0,748
31
+ cornflow/commands/access.py,sha256=4as74PKwcq5VoPnT9Hh2uUEDQLOQo6jAelwmeBIPW3c,1013
32
32
  cornflow/commands/actions.py,sha256=4AwgAmyI6VeaugkISvTlNGrIzMMU_-ZB3MhwDD_CIEA,1544
33
+ cornflow/commands/auxiliar.py,sha256=s1p5K-v5lBG3wivB8snl_32aiRQbgXIUmbSjKMs8Cm4,3436
33
34
  cornflow/commands/cleanup.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
35
  cornflow/commands/dag.py,sha256=AtagFGnB_ucfO0qUawDgd4iRoBCVc-RiOs08DqXSwXM,3786
35
- cornflow/commands/permissions.py,sha256=xrAVNjipabc1SWt3TApRdoVsHjtfmpEPnC6vXmmhLWs,7412
36
- cornflow/commands/roles.py,sha256=Oux-UkswkQ74zqaMEJYIEsZpQZGBcGaSahVzx9feAHU,1516
36
+ cornflow/commands/permissions.py,sha256=8olHdnEGAP4gSm4ati675NNkZZWEUg5P5HcJ8Ft-B2w,9799
37
+ cornflow/commands/roles.py,sha256=c-sPPz_3Zb3c7AekXhr7gE264I_BK6LpR2wSa80Eg7M,1602
37
38
  cornflow/commands/schemas.py,sha256=40dZSJ2nEqBa7Crb6DbFmnclT5e8ljAIjscOgHr9lhk,1970
38
39
  cornflow/commands/users.py,sha256=2YTbNYY5kZL6ujxGP4fyYgqtv5uuVGdkLR31n7OFFaE,2477
39
- cornflow/commands/views.py,sha256=K2Ld1-l1ZKn9m6e2W1LCxmN44QokwR-8u8rIrviiEf8,2276
40
+ cornflow/commands/views.py,sha256=yg3vuKoJwusBWm0v9PCGYr1xAbL-dWAi4aDV0aGpMnA,6978
40
41
  cornflow/endpoints/__init__.py,sha256=ZlwhY8MiynQ0BdATkrsikGM2Kqo4DPxkVTc3faNfzRY,7492
41
42
  cornflow/endpoints/action.py,sha256=ksHK3F919cjkONLcFV2tUIbG-eZw5XbYkqVjYx9iq5I,1359
42
43
  cornflow/endpoints/alarms.py,sha256=M-fpRAm5ZgYyZXvcgS0NHkeMGnvcbfQh2O5qQJUaeoM,4372
@@ -119,7 +120,7 @@ cornflow/schemas/user_role.py,sha256=e5y6RgdZZtLqD-h2B3sa5WokI5-pT78tWw85IG34I74
119
120
  cornflow/schemas/view.py,sha256=ctq9Y1TmjrWdyOqgDYeEx7qbbuNLKfSiNOlFTlXmpaw,429
120
121
  cornflow/shared/__init__.py,sha256=1ahcBwWOsSjGI4FEm77JBQjitBdBszOncKcEMjzwGYE,29
121
122
  cornflow/shared/compress.py,sha256=pohQaGs1xbH8CN6URIH6BAHA--pFq7Hmjz8oI3c3B5c,1347
122
- cornflow/shared/const.py,sha256=iyD9RFf0z7NyGvZJtNGLIAbsxYYcG3IpRbke72pl-7A,3555
123
+ cornflow/shared/const.py,sha256=Oq2WnV-tHlU2EFfxSEMIhd6Ihsf_OuVbdU1E4DTps3Q,3556
123
124
  cornflow/shared/email.py,sha256=QNDDMv86LZObkevSCyUbLQeR2UD3zWScPIr82NDzYHQ,3437
124
125
  cornflow/shared/exceptions.py,sha256=E82488IiwTXCv8iwrnGvkTonhJcwbeE5ARO4Zsmhl2c,6966
125
126
  cornflow/shared/licenses.py,sha256=Lc71Jw2NxVTFWtoXdQ9wJX_o3BDfYg1xVoehDXvnCkQ,1328
@@ -129,7 +130,7 @@ cornflow/shared/utils.py,sha256=g2ZsD3SwsqIHXZ7GWVAVB0F9gX7mT9dQkPgR2Ahsh6M,860
129
130
  cornflow/shared/utils_tables.py,sha256=JnyaQ-5d1alj230nir-6elC2i0QX-u-_32SAs2eDtC0,3298
130
131
  cornflow/shared/validators.py,sha256=Iy2jaGzS9ojqlTE_-vKH4oQKD89GN5C0EPEZMg6UZgc,4761
131
132
  cornflow/shared/authentication/__init__.py,sha256=cJIChk5X6hbA_16usEvfHr8g4JDFI6WKo0GPVOOiYHA,137
132
- cornflow/shared/authentication/auth.py,sha256=Am4981BchXnwGCUaBg6DlG50TDaT-900KAQ8V-F6umE,18220
133
+ cornflow/shared/authentication/auth.py,sha256=SiXMCHwyAXHL7Krd5Ubht16NPNXqSvf-85xyiEB2DK4,18247
133
134
  cornflow/shared/authentication/decorators.py,sha256=_QpwOU1kYzpaK85Dl0Btdj5hG8Ps47PFgySp_gqhlgk,1276
134
135
  cornflow/shared/authentication/ldap.py,sha256=QfdC2X_ZMcIJabKC5pYWDGMhS5pIOJJvdZXuuiruq-M,4853
135
136
  cornflow/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -148,11 +149,12 @@ cornflow/tests/unit/test_apiview.py,sha256=03M1GsQRVK7zqmslhOJXr4lLDLY2gMAgg86nk
148
149
  cornflow/tests/unit/test_application.py,sha256=ZVmTQDUOkPRxHqt6mWU9G_lQ3jJNMJR0cx7IkLMFGrU,1715
149
150
  cornflow/tests/unit/test_cases.py,sha256=Ez9dxlZL-SUf9DW9b_A_qPowHqUZ-TA73DMOzeBeLIU,37718
150
151
  cornflow/tests/unit/test_cli.py,sha256=E2w-Lzgx_k__0mYwlbg2z80_z9nwPZKI0CbgyGmpQRY,18775
151
- cornflow/tests/unit/test_commands.py,sha256=EcaZh1DsA3HgHx5NhpIJKcnUn8KDcnyk2QajhwGs6Yk,21063
152
+ cornflow/tests/unit/test_commands.py,sha256=kvO8Vn60rj3WBG2oXw7NpDSEYoGLNe806txbJPhtNJo,14722
152
153
  cornflow/tests/unit/test_dags.py,sha256=XsOi5bBJQdQz3DmYAVJf1myoAsRyBBdmku-xBr0Bku0,13386
153
154
  cornflow/tests/unit/test_data_checks.py,sha256=6s50d1iuRTUcAYn14oEcRS39ZZ6E9ussU4YpkpYhtC4,8612
154
155
  cornflow/tests/unit/test_example_data.py,sha256=D-Tgnqw7NZlnBXaDcUU0reNhAca5JlJP2Sdn3KdS4Sw,4127
155
156
  cornflow/tests/unit/test_executions.py,sha256=fC9kMRqU3qMGQ9eH6WXlOkU8CKJe6l-DlAKln4ImCSU,18192
157
+ cornflow/tests/unit/test_external_role_creation.py,sha256=pVkEUTYxc_qt9weo_EKE-_AOnT2v8Z1mkSUOFsVDliI,21172
156
158
  cornflow/tests/unit/test_generate_from_schema.py,sha256=L1EdnASbDJ8SjrX1V4WnUKKwV0sRTwVnNYnxSpyeSeQ,15376
157
159
  cornflow/tests/unit/test_health.py,sha256=xT0OcnpxbxE0QJlyg4r1qCYDn-hwuk0Ynw5JA4cJtsY,1205
158
160
  cornflow/tests/unit/test_instances.py,sha256=Mf9jijQOcDE3ylPfMTnVRocRegcugEdCnoMCqSmKKqQ,11083
@@ -169,8 +171,8 @@ cornflow/tests/unit/test_tables.py,sha256=SW_K8LRLwR1nB0uH8CPQCjeN8Gei-TasAgkOin
169
171
  cornflow/tests/unit/test_token.py,sha256=PZ11b46UCQpCESsRiAPhpgWkGAsAwKCVNxVQai_kxXM,4199
170
172
  cornflow/tests/unit/test_users.py,sha256=N5tcF5nSncD0F_ZlBxGuS87p6kNS4hUzRLr3_AcnK-o,22802
171
173
  cornflow/tests/unit/tools.py,sha256=ag3sWv2WLi498R1GL5AOUnXqSsszD3UugzLZLC5NqAw,585
172
- cornflow-1.2.3a2.dist-info/METADATA,sha256=FFezXKR-SO1Jiu61BODNGQ8Bkva4mCNw02isDt9XwEc,9529
173
- cornflow-1.2.3a2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
174
- cornflow-1.2.3a2.dist-info/entry_points.txt,sha256=q9cPKAFBsmHkERCqQ2JcOTM-tVBLHTl-DGxwCXowAWM,46
175
- cornflow-1.2.3a2.dist-info/top_level.txt,sha256=Qj9kLFJW1PLb-ZV2s_aCkQ-Wi5W6KC6fFR-LTBrx-rU,24
176
- cornflow-1.2.3a2.dist-info/RECORD,,
174
+ cornflow-1.2.3a4.dist-info/METADATA,sha256=m7K67ojro9FvxpT3XWKYd2NQSTkkGIqhNB-UhU_Oq_k,9532
175
+ cornflow-1.2.3a4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
176
+ cornflow-1.2.3a4.dist-info/entry_points.txt,sha256=q9cPKAFBsmHkERCqQ2JcOTM-tVBLHTl-DGxwCXowAWM,46
177
+ cornflow-1.2.3a4.dist-info/top_level.txt,sha256=Qj9kLFJW1PLb-ZV2s_aCkQ-Wi5W6KC6fFR-LTBrx-rU,24
178
+ cornflow-1.2.3a4.dist-info/RECORD,,