infrahub-server 1.1.6__py3-none-any.whl → 1.2.0rc0__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 (346) hide show
  1. infrahub/api/artifact.py +16 -4
  2. infrahub/api/dependencies.py +8 -0
  3. infrahub/api/oauth2.py +0 -1
  4. infrahub/api/oidc.py +0 -1
  5. infrahub/api/query.py +18 -7
  6. infrahub/api/schema.py +32 -6
  7. infrahub/api/transformation.py +12 -5
  8. infrahub/{message_bus/messages/check_artifact_create.py → artifacts/models.py} +2 -4
  9. infrahub/{message_bus/operations/check/artifact.py → artifacts/tasks.py} +26 -25
  10. infrahub/cli/__init__.py +0 -2
  11. infrahub/cli/db.py +6 -7
  12. infrahub/cli/events.py +8 -3
  13. infrahub/cli/git_agent.py +9 -7
  14. infrahub/cli/tasks.py +4 -6
  15. infrahub/computed_attribute/tasks.py +63 -17
  16. infrahub/computed_attribute/triggers.py +90 -0
  17. infrahub/config.py +1 -1
  18. infrahub/context.py +39 -0
  19. infrahub/core/account.py +5 -8
  20. infrahub/core/attribute.py +53 -21
  21. infrahub/core/branch/models.py +4 -4
  22. infrahub/core/branch/tasks.py +89 -130
  23. infrahub/core/changelog/__init__.py +0 -0
  24. infrahub/core/changelog/diff.py +232 -0
  25. infrahub/core/changelog/models.py +488 -0
  26. infrahub/core/constants/__init__.py +19 -2
  27. infrahub/core/constants/infrahubkind.py +1 -0
  28. infrahub/core/diff/combiner.py +12 -8
  29. infrahub/core/diff/coordinator.py +49 -70
  30. infrahub/core/diff/data_check_synchronizer.py +86 -7
  31. infrahub/core/diff/enricher/aggregated.py +3 -3
  32. infrahub/core/diff/enricher/cardinality_one.py +2 -7
  33. infrahub/core/diff/enricher/hierarchy.py +5 -3
  34. infrahub/core/diff/enricher/labels.py +14 -4
  35. infrahub/core/diff/enricher/path_identifier.py +3 -9
  36. infrahub/core/diff/enricher/summary_counts.py +3 -1
  37. infrahub/core/diff/merger/merger.py +8 -4
  38. infrahub/core/diff/model/path.py +47 -29
  39. infrahub/core/diff/query/all_conflicts.py +6 -3
  40. infrahub/core/diff/query/artifact.py +1 -1
  41. infrahub/core/diff/query/delete_query.py +1 -1
  42. infrahub/core/diff/query/diff_get.py +3 -2
  43. infrahub/core/diff/query/diff_summary.py +1 -1
  44. infrahub/core/diff/query/field_specifiers.py +3 -1
  45. infrahub/core/diff/query/field_summary.py +3 -2
  46. infrahub/core/diff/query/filters.py +12 -1
  47. infrahub/core/diff/query/get_conflict_query.py +1 -1
  48. infrahub/core/diff/query/has_conflicts_query.py +6 -3
  49. infrahub/core/diff/query/merge.py +3 -3
  50. infrahub/core/diff/query/{drop_tracking_id.py → merge_tracking_id.py} +4 -4
  51. infrahub/core/diff/query/roots_metadata.py +9 -2
  52. infrahub/core/diff/query/save.py +151 -66
  53. infrahub/core/diff/query/summary_counts_enricher.py +220 -0
  54. infrahub/core/diff/query/time_range_query.py +3 -2
  55. infrahub/core/diff/query/update_conflict_query.py +1 -1
  56. infrahub/core/diff/query_parser.py +49 -24
  57. infrahub/core/diff/repository/deserializer.py +24 -25
  58. infrahub/core/diff/repository/repository.py +76 -20
  59. infrahub/core/diff/tasks.py +9 -8
  60. infrahub/core/enums.py +1 -1
  61. infrahub/core/integrity/object_conflict/conflict_recorder.py +1 -1
  62. infrahub/core/ipam/reconciler.py +1 -1
  63. infrahub/core/ipam/tasks.py +2 -3
  64. infrahub/core/manager.py +18 -13
  65. infrahub/core/merge.py +5 -2
  66. infrahub/core/migrations/graph/m001_add_version_to_graph.py +1 -1
  67. infrahub/core/migrations/graph/m002_attribute_is_default.py +2 -2
  68. infrahub/core/migrations/graph/m003_relationship_parent_optional.py +2 -2
  69. infrahub/core/migrations/graph/m004_add_attr_documentation.py +1 -1
  70. infrahub/core/migrations/graph/m005_add_rel_read_only.py +1 -1
  71. infrahub/core/migrations/graph/m006_add_rel_on_delete.py +1 -1
  72. infrahub/core/migrations/graph/m007_add_rel_allow_override.py +1 -1
  73. infrahub/core/migrations/graph/m008_add_human_friendly_id.py +1 -1
  74. infrahub/core/migrations/graph/m009_add_generate_profile_attr.py +1 -1
  75. infrahub/core/migrations/graph/m010_add_generate_profile_attr_generic.py +1 -1
  76. infrahub/core/migrations/graph/m011_remove_profile_relationship_schema.py +2 -2
  77. infrahub/core/migrations/graph/m012_convert_account_generic.py +12 -23
  78. infrahub/core/migrations/graph/m013_convert_git_password_credential.py +7 -11
  79. infrahub/core/migrations/graph/m014_remove_index_attr_value.py +2 -2
  80. infrahub/core/migrations/graph/m015_diff_format_update.py +1 -1
  81. infrahub/core/migrations/graph/m016_diff_delete_bug_fix.py +1 -1
  82. infrahub/core/migrations/graph/m017_add_core_profile.py +1 -1
  83. infrahub/core/migrations/graph/m018_uniqueness_nulls.py +2 -2
  84. infrahub/core/migrations/query/attribute_add.py +1 -1
  85. infrahub/core/migrations/query/attribute_rename.py +1 -1
  86. infrahub/core/migrations/query/delete_element_in_schema.py +1 -1
  87. infrahub/core/migrations/query/node_duplicate.py +1 -1
  88. infrahub/core/migrations/query/relationship_duplicate.py +1 -1
  89. infrahub/core/migrations/query/schema_attribute_update.py +1 -1
  90. infrahub/core/migrations/schema/node_attribute_remove.py +1 -1
  91. infrahub/core/migrations/schema/node_remove.py +1 -1
  92. infrahub/core/migrations/schema/tasks.py +5 -5
  93. infrahub/core/migrations/shared.py +4 -4
  94. infrahub/core/models.py +7 -8
  95. infrahub/core/node/__init__.py +161 -40
  96. infrahub/core/node/base.py +1 -1
  97. infrahub/core/node/constraints/grouped_uniqueness.py +9 -2
  98. infrahub/core/node/delete_validator.py +4 -4
  99. infrahub/core/node/ipam.py +13 -8
  100. infrahub/core/node/permissions.py +4 -0
  101. infrahub/core/node/resource_manager/ip_prefix_pool.py +8 -5
  102. infrahub/core/node/standard.py +3 -5
  103. infrahub/core/property.py +1 -1
  104. infrahub/core/protocols.py +4 -0
  105. infrahub/core/protocols_base.py +4 -2
  106. infrahub/core/query/__init__.py +2 -5
  107. infrahub/core/query/attribute.py +9 -9
  108. infrahub/core/query/branch.py +5 -5
  109. infrahub/core/query/delete.py +1 -1
  110. infrahub/core/query/diff.py +45 -7
  111. infrahub/core/query/ipam.py +4 -4
  112. infrahub/core/query/node.py +19 -14
  113. infrahub/core/query/relationship.py +10 -11
  114. infrahub/core/query/resource_manager.py +13 -11
  115. infrahub/core/query/standard_node.py +6 -6
  116. infrahub/core/query/task.py +3 -3
  117. infrahub/core/query/task_log.py +1 -1
  118. infrahub/core/query/utils.py +5 -5
  119. infrahub/core/registry.py +0 -2
  120. infrahub/core/relationship/constraints/count.py +1 -1
  121. infrahub/core/relationship/constraints/peer_kind.py +1 -1
  122. infrahub/core/relationship/model.py +66 -26
  123. infrahub/core/schema/__init__.py +6 -4
  124. infrahub/core/schema/basenode_schema.py +1 -3
  125. infrahub/core/schema/definitions/core.py +14 -2
  126. infrahub/core/schema/definitions/internal.py +16 -0
  127. infrahub/core/schema/generated/genericnode_schema.py +5 -0
  128. infrahub/core/schema/generated/node_schema.py +5 -0
  129. infrahub/core/schema/generic_schema.py +5 -1
  130. infrahub/core/schema/manager.py +45 -42
  131. infrahub/core/schema/node_schema.py +4 -0
  132. infrahub/core/schema/profile_schema.py +4 -0
  133. infrahub/core/schema/relationship_schema.py +2 -2
  134. infrahub/core/schema/schema_branch.py +248 -14
  135. infrahub/core/schema/template_schema.py +36 -0
  136. infrahub/core/task/user_task.py +7 -5
  137. infrahub/core/timestamp.py +1 -1
  138. infrahub/core/utils.py +3 -2
  139. infrahub/core/validators/attribute/choices.py +1 -1
  140. infrahub/core/validators/attribute/enum.py +1 -1
  141. infrahub/core/validators/attribute/kind.py +1 -1
  142. infrahub/core/validators/attribute/length.py +1 -1
  143. infrahub/core/validators/attribute/optional.py +1 -1
  144. infrahub/core/validators/attribute/regex.py +1 -1
  145. infrahub/core/validators/attribute/unique.py +1 -1
  146. infrahub/core/validators/checks_runner.py +37 -0
  147. infrahub/core/validators/node/generate_profile.py +1 -1
  148. infrahub/core/validators/node/hierarchy.py +1 -1
  149. infrahub/core/validators/query.py +1 -1
  150. infrahub/core/validators/relationship/count.py +1 -1
  151. infrahub/core/validators/relationship/optional.py +1 -1
  152. infrahub/core/validators/relationship/peer.py +1 -1
  153. infrahub/core/validators/tasks.py +8 -6
  154. infrahub/core/validators/uniqueness/query.py +20 -17
  155. infrahub/database/__init__.py +15 -2
  156. infrahub/database/memgraph.py +1 -1
  157. infrahub/dependencies/builder/constraint/grouped/node_runner.py +0 -2
  158. infrahub/dependencies/builder/diff/combiner.py +1 -1
  159. infrahub/dependencies/builder/diff/conflicts_enricher.py +1 -1
  160. infrahub/dependencies/builder/diff/coordinator.py +0 -2
  161. infrahub/dependencies/builder/diff/deserializer.py +1 -1
  162. infrahub/dependencies/builder/diff/enricher/summary_counts.py +1 -1
  163. infrahub/events/branch_action.py +47 -21
  164. infrahub/events/group_action.py +73 -0
  165. infrahub/events/models.py +159 -51
  166. infrahub/events/node_action.py +74 -8
  167. infrahub/events/repository_action.py +8 -8
  168. infrahub/events/schema_action.py +21 -8
  169. infrahub/generators/tasks.py +12 -13
  170. infrahub/git/base.py +3 -5
  171. infrahub/git/constants.py +0 -1
  172. infrahub/git/integrator.py +36 -35
  173. infrahub/git/repository.py +7 -8
  174. infrahub/git/tasks.py +43 -107
  175. infrahub/git_credential/helper.py +2 -3
  176. infrahub/graphql/analyzer.py +572 -11
  177. infrahub/graphql/app.py +34 -26
  178. infrahub/graphql/auth/query_permission_checker/anonymous_checker.py +5 -5
  179. infrahub/graphql/auth/query_permission_checker/default_branch_checker.py +4 -4
  180. infrahub/graphql/auth/query_permission_checker/merge_operation_checker.py +4 -4
  181. infrahub/graphql/auth/query_permission_checker/object_permission_checker.py +28 -35
  182. infrahub/graphql/auth/query_permission_checker/super_admin_checker.py +5 -5
  183. infrahub/graphql/enums.py +1 -1
  184. infrahub/graphql/initialization.py +5 -1
  185. infrahub/graphql/loaders/node.py +2 -2
  186. infrahub/graphql/manager.py +59 -54
  187. infrahub/graphql/mutations/account.py +20 -13
  188. infrahub/graphql/mutations/artifact_definition.py +16 -12
  189. infrahub/graphql/mutations/branch.py +61 -40
  190. infrahub/graphql/mutations/computed_attribute.py +19 -13
  191. infrahub/graphql/mutations/diff.py +37 -9
  192. infrahub/graphql/mutations/diff_conflict.py +9 -8
  193. infrahub/graphql/mutations/graphql_query.py +19 -11
  194. infrahub/graphql/mutations/ipam.py +21 -19
  195. infrahub/graphql/mutations/main.py +197 -44
  196. infrahub/graphql/mutations/menu.py +8 -8
  197. infrahub/graphql/mutations/proposed_change.py +36 -28
  198. infrahub/graphql/mutations/relationship.py +302 -105
  199. infrahub/graphql/mutations/repository.py +41 -35
  200. infrahub/graphql/mutations/resource_manager.py +26 -26
  201. infrahub/graphql/mutations/schema.py +51 -33
  202. infrahub/graphql/mutations/tasks.py +16 -10
  203. infrahub/graphql/parser.py +1 -1
  204. infrahub/graphql/permissions.py +6 -4
  205. infrahub/graphql/queries/account.py +22 -18
  206. infrahub/graphql/queries/branch.py +6 -4
  207. infrahub/graphql/queries/diff/tree.py +48 -42
  208. infrahub/graphql/queries/event.py +112 -0
  209. infrahub/graphql/queries/internal.py +3 -3
  210. infrahub/graphql/queries/ipam.py +23 -18
  211. infrahub/graphql/queries/relationship.py +11 -10
  212. infrahub/graphql/queries/resource_manager.py +43 -27
  213. infrahub/graphql/queries/search.py +9 -8
  214. infrahub/graphql/queries/status.py +12 -9
  215. infrahub/graphql/queries/task.py +11 -9
  216. infrahub/graphql/resolvers/resolver.py +69 -43
  217. infrahub/graphql/resolvers/single_relationship.py +16 -10
  218. infrahub/graphql/schema.py +2 -0
  219. infrahub/graphql/subscription/__init__.py +1 -1
  220. infrahub/graphql/subscription/events.py +1 -1
  221. infrahub/graphql/subscription/graphql_query.py +8 -8
  222. infrahub/graphql/types/branch.py +2 -2
  223. infrahub/graphql/types/common.py +6 -1
  224. infrahub/graphql/types/enums.py +2 -0
  225. infrahub/graphql/types/event.py +100 -0
  226. infrahub/graphql/types/interface.py +2 -2
  227. infrahub/graphql/types/node.py +3 -3
  228. infrahub/graphql/types/permission.py +2 -2
  229. infrahub/graphql/types/relationship.py +3 -3
  230. infrahub/graphql/types/standard_node.py +9 -11
  231. infrahub/graphql/utils.py +28 -182
  232. infrahub/groups/tasks.py +2 -3
  233. infrahub/lock.py +1 -1
  234. infrahub/menu/constants.py +1 -0
  235. infrahub/menu/generator.py +14 -3
  236. infrahub/menu/menu.py +116 -127
  237. infrahub/menu/models.py +4 -4
  238. infrahub/message_bus/messages/__init__.py +0 -4
  239. infrahub/message_bus/messages/event_branch_merge.py +3 -0
  240. infrahub/message_bus/messages/request_proposedchange_pipeline.py +2 -0
  241. infrahub/message_bus/operations/__init__.py +3 -5
  242. infrahub/message_bus/operations/check/__init__.py +2 -2
  243. infrahub/message_bus/operations/check/generator.py +1 -3
  244. infrahub/message_bus/operations/check/repository.py +1 -1
  245. infrahub/message_bus/operations/event/branch.py +7 -3
  246. infrahub/message_bus/operations/event/schema.py +1 -1
  247. infrahub/message_bus/operations/finalize/validator.py +1 -1
  248. infrahub/message_bus/operations/git/file.py +2 -2
  249. infrahub/message_bus/operations/git/repository.py +1 -1
  250. infrahub/message_bus/operations/requests/__init__.py +0 -2
  251. infrahub/message_bus/operations/requests/generator_definition.py +1 -1
  252. infrahub/message_bus/operations/requests/proposed_change.py +26 -11
  253. infrahub/message_bus/operations/requests/repository.py +2 -2
  254. infrahub/message_bus/operations/send/echo.py +1 -1
  255. infrahub/message_bus/types.py +1 -1
  256. infrahub/permissions/__init__.py +2 -1
  257. infrahub/permissions/types.py +26 -0
  258. infrahub/pools/prefix.py +29 -165
  259. infrahub/prefect_server/__init__.py +0 -0
  260. infrahub/prefect_server/app.py +18 -0
  261. infrahub/prefect_server/database.py +20 -0
  262. infrahub/prefect_server/events.py +28 -0
  263. infrahub/prefect_server/models.py +46 -0
  264. infrahub/proposed_change/models.py +15 -1
  265. infrahub/proposed_change/tasks.py +173 -35
  266. infrahub/pytest_plugin.py +4 -4
  267. infrahub/server.py +12 -11
  268. infrahub/services/__init__.py +147 -62
  269. infrahub/services/adapters/cache/__init__.py +7 -5
  270. infrahub/services/adapters/cache/nats.py +40 -22
  271. infrahub/services/adapters/cache/redis.py +0 -4
  272. infrahub/services/adapters/event/__init__.py +10 -18
  273. infrahub/services/adapters/http/__init__.py +0 -5
  274. infrahub/services/adapters/http/httpx.py +22 -15
  275. infrahub/services/adapters/message_bus/__init__.py +23 -6
  276. infrahub/services/adapters/message_bus/local.py +8 -6
  277. infrahub/services/adapters/message_bus/nats.py +12 -6
  278. infrahub/services/adapters/message_bus/rabbitmq.py +22 -9
  279. infrahub/services/adapters/workflow/__init__.py +11 -8
  280. infrahub/services/adapters/workflow/local.py +28 -7
  281. infrahub/services/adapters/workflow/worker.py +23 -7
  282. infrahub/services/component.py +38 -35
  283. infrahub/services/scheduler.py +32 -29
  284. infrahub/storage.py +2 -4
  285. infrahub/task_manager/constants.py +1 -1
  286. infrahub/task_manager/event.py +182 -0
  287. infrahub/task_manager/models.py +125 -1
  288. infrahub/task_manager/task.py +1 -1
  289. infrahub/tasks/artifact.py +14 -16
  290. infrahub/tasks/registry.py +1 -1
  291. infrahub/tasks/telemetry.py +13 -14
  292. infrahub/transformations/tasks.py +3 -5
  293. infrahub/trigger/__init__.py +0 -0
  294. infrahub/trigger/catalogue.py +15 -0
  295. infrahub/trigger/constants.py +9 -0
  296. infrahub/trigger/models.py +69 -0
  297. infrahub/trigger/tasks.py +85 -0
  298. infrahub/types.py +1 -1
  299. infrahub/utils.py +1 -1
  300. infrahub/webhook/constants.py +0 -2
  301. infrahub/webhook/models.py +8 -2
  302. infrahub/webhook/tasks.py +20 -73
  303. infrahub/webhook/triggers.py +20 -0
  304. infrahub/workers/infrahub_async.py +36 -25
  305. infrahub/workers/utils.py +63 -0
  306. infrahub/workflows/catalogue.py +13 -37
  307. infrahub/workflows/initialization.py +6 -8
  308. infrahub/workflows/models.py +3 -5
  309. infrahub/workflows/utils.py +1 -1
  310. infrahub_sdk/ctl/check.py +3 -3
  311. infrahub_sdk/ctl/cli_commands.py +11 -10
  312. infrahub_sdk/ctl/exceptions.py +0 -6
  313. infrahub_sdk/ctl/exporter.py +1 -1
  314. infrahub_sdk/ctl/generator.py +5 -5
  315. infrahub_sdk/ctl/importer.py +3 -2
  316. infrahub_sdk/ctl/menu.py +1 -1
  317. infrahub_sdk/ctl/object.py +1 -1
  318. infrahub_sdk/ctl/repository.py +23 -15
  319. infrahub_sdk/ctl/schema.py +2 -2
  320. infrahub_sdk/ctl/utils.py +4 -3
  321. infrahub_sdk/ctl/validate.py +2 -1
  322. infrahub_sdk/exceptions.py +6 -0
  323. infrahub_sdk/generator.py +3 -0
  324. infrahub_sdk/node.py +2 -2
  325. infrahub_sdk/schema/__init__.py +14 -2
  326. infrahub_sdk/schema/main.py +7 -0
  327. infrahub_sdk/utils.py +11 -1
  328. infrahub_sdk/yaml.py +2 -3
  329. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/METADATA +46 -12
  330. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/RECORD +338 -321
  331. infrahub_testcontainers/container.py +14 -6
  332. infrahub_testcontainers/docker-compose.test.yml +24 -5
  333. infrahub_testcontainers/haproxy.cfg +43 -0
  334. infrahub_testcontainers/helpers.py +85 -1
  335. infrahub/core/branch/constants.py +0 -2
  336. infrahub/graphql/query.py +0 -52
  337. infrahub/message_bus/messages/request_artifactdefinition_check.py +0 -17
  338. infrahub/message_bus/operations/requests/artifact_definition.py +0 -148
  339. infrahub/schema/constants.py +0 -1
  340. infrahub/schema/tasks.py +0 -76
  341. infrahub/services/adapters/database/__init__.py +0 -9
  342. infrahub_sdk/ctl/_file.py +0 -13
  343. /infrahub/{schema → artifacts}/__init__.py +0 -0
  344. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/LICENSE.txt +0 -0
  345. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/WHEEL +0 -0
  346. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/entry_points.txt +0 -0
infrahub/graphql/app.py CHANGED
@@ -76,8 +76,6 @@ if TYPE_CHECKING:
76
76
 
77
77
  from .auth.query_permission_checker.checker import GraphQLQueryPermissionChecker
78
78
 
79
- # pylint: disable=unused-argument,raise-missing-from
80
-
81
79
 
82
80
  GQL_CONNECTION_ACK = "connection_ack"
83
81
  GQL_CONNECTION_ERROR = "connection_error"
@@ -208,13 +206,32 @@ class InfrahubGraphQLApp:
208
206
  graphql_params = await prepare_graphql_params(
209
207
  db=db, branch=branch, at=at, account_session=account_session, request=request
210
208
  )
209
+ schema_branch = db.schema.get_schema_branch(name=branch.name)
210
+
211
211
  analyzed_query = InfrahubGraphQLQueryAnalyzer(
212
212
  query=query,
213
+ schema_branch=schema_branch,
213
214
  query_variables=variable_values,
214
215
  schema=graphql_params.schema,
215
216
  operation_name=operation_name,
216
217
  branch=branch,
217
218
  )
219
+
220
+ # if the query contains some mutation, it's not currently supported to set AT manually
221
+ if analyzed_query.contains_mutation:
222
+ graphql_params.context.at = Timestamp()
223
+ elif at and branch.schema_changed_at and Timestamp(branch.schema_changed_at) > Timestamp(at):
224
+ schema_branch = await registry.schema.load_schema_from_db(db=db, branch=branch, at=Timestamp(at))
225
+ db.add_schema(name=branch.name, schema=schema_branch)
226
+ analyzed_query = InfrahubGraphQLQueryAnalyzer(
227
+ query=query,
228
+ schema_branch=schema_branch,
229
+ query_variables=variable_values,
230
+ schema=graphql_params.schema,
231
+ operation_name=operation_name,
232
+ branch=branch,
233
+ )
234
+
218
235
  await self._evaluate_permissions(
219
236
  db=db,
220
237
  request=request,
@@ -224,13 +241,6 @@ class InfrahubGraphQLApp:
224
241
  branch=branch,
225
242
  )
226
243
 
227
- # if the query contains some mutation, it's not currently supported to set AT manually
228
- if analyzed_query.contains_mutation:
229
- graphql_params.context.at = Timestamp()
230
- elif at and branch.schema_changed_at and Timestamp(branch.schema_changed_at) > Timestamp(at):
231
- schema_branch = await registry.schema.load_schema_from_db(db=db, branch=branch, at=Timestamp(at))
232
- db.add_schema(name=branch.name, schema=schema_branch)
233
-
234
244
  if operation_name == "IntrospectionQuery":
235
245
  nbr_object_in_schema = len(graphql_params.schema.type_map)
236
246
  self.logger.debug(
@@ -272,17 +282,15 @@ class InfrahubGraphQLApp:
272
282
  GRAPHQL_QUERY_HEIGHT_METRICS.labels(**labels).observe(await analyzed_query.calculate_height())
273
283
  # GRAPHQL_QUERY_VARS_METRICS.labels(**labels).observe(len(analyzed_query.variables))
274
284
  GRAPHQL_TOP_LEVEL_QUERIES_METRICS.labels(**labels).observe(analyzed_query.nbr_queries)
275
- GRAPHQL_QUERY_OBJECTS_METRICS.labels(**labels).observe(
276
- len(await analyzed_query.get_models_in_use(types=graphql_params.context.types))
277
- )
285
+ GRAPHQL_QUERY_OBJECTS_METRICS.labels(**labels).observe(len(analyzed_query.query_report.impacted_models))
278
286
 
279
- valid, errors = analyzed_query.is_valid
280
- if not valid:
287
+ _, errors = analyzed_query.is_valid
288
+ if errors:
281
289
  GRAPHQL_QUERY_ERRORS_METRICS.labels(**labels).observe(len(errors))
282
290
 
283
291
  return json_response
284
292
 
285
- def _set_labels(self, request: Request, branch: Branch, query: InfrahubGraphQLQueryAnalyzer) -> dict[str, Any]:
293
+ def _set_labels(self, request: Request, branch: Branch, query: InfrahubGraphQLQueryAnalyzer) -> dict[str, Any]: # noqa: ARG002
286
294
  return {
287
295
  "type": "mutation" if query.contains_mutation else "query",
288
296
  "branch": branch.name,
@@ -293,7 +301,7 @@ class InfrahubGraphQLApp:
293
301
 
294
302
  async def _evaluate_permissions(
295
303
  self,
296
- request: Request,
304
+ request: Request, # noqa: ARG002
297
305
  db: InfrahubDatabase,
298
306
  query: InfrahubGraphQLQueryAnalyzer,
299
307
  query_parameters: GraphqlParams,
@@ -450,7 +458,7 @@ class InfrahubGraphQLApp:
450
458
  async for result in asyncgen:
451
459
  payload = {"data": result.data}
452
460
  await websocket.send_json({"type": GQL_DATA, "id": operation_id, "payload": payload})
453
- except Exception as error: # pylint: disable=broad-exception-caught
461
+ except Exception as error:
454
462
  if not isinstance(error, GraphQLError):
455
463
  self.logger.error("An exception occurred in resolvers", exc_info=error)
456
464
  error = GraphQLError(str(error), original_error=error)
@@ -471,8 +479,8 @@ async def _get_operation_from_request(request: Request) -> Union[dict[str, Any],
471
479
  if content_type == "application/json":
472
480
  try:
473
481
  return cast(Union[dict[str, Any], list[Any]], await request.json())
474
- except (TypeError, ValueError):
475
- raise ValueError("Request body is not a valid JSON")
482
+ except (TypeError, ValueError) as err:
483
+ raise ValueError("Request body is not a valid JSON") from err
476
484
  elif content_type == "multipart/form-data":
477
485
  return await _get_operation_from_multipart(request)
478
486
  else:
@@ -482,24 +490,24 @@ async def _get_operation_from_request(request: Request) -> Union[dict[str, Any],
482
490
  async def _get_operation_from_multipart(request: Request) -> Union[dict[str, Any], list[Any]]:
483
491
  try:
484
492
  request_body = await request.form()
485
- except Exception:
486
- raise ValueError("Request body is not a valid multipart/form-data")
493
+ except Exception as err:
494
+ raise ValueError("Request body is not a valid multipart/form-data") from err
487
495
 
488
496
  try:
489
497
  operations_value = request_body.get("operations")
490
498
  operations_data = operations_value if isinstance(operations_value, str) else ""
491
499
  operations = ujson.loads(operations_data)
492
- except (TypeError, ValueError):
493
- raise ValueError("'operations' must be a valid JSON")
494
- if not isinstance(operations, (dict, list)):
500
+ except (TypeError, ValueError) as err:
501
+ raise ValueError("'operations' must be a valid JSON") from err
502
+ if not isinstance(operations, dict | list):
495
503
  raise ValueError("'operations' field must be an Object or an Array")
496
504
 
497
505
  try:
498
506
  map_value = request_body.get("map")
499
507
  map_data = map_value if isinstance(map_value, str) else ""
500
508
  name_path_map = ujson.loads(map_data)
501
- except (TypeError, ValueError):
502
- raise ValueError("'map' field must be a valid JSON")
509
+ except (TypeError, ValueError) as err:
510
+ raise ValueError("'map' field must be a valid JSON") from err
503
511
  if not isinstance(name_path_map, dict):
504
512
  raise ValueError("'map' field must be an Object")
505
513
 
@@ -14,16 +14,16 @@ class AnonymousGraphQLPermissionChecker(GraphQLQueryPermissionCheckerInterface):
14
14
  def __init__(self, anonymous_access_allowed_func: Callable[[], bool]) -> None:
15
15
  self.anonymous_access_allowed_func = anonymous_access_allowed_func
16
16
 
17
- async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool:
17
+ async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: # noqa: ARG002
18
18
  return not account_session.authenticated
19
19
 
20
20
  async def check(
21
21
  self,
22
- db: InfrahubDatabase,
23
- account_session: AccountSession,
22
+ db: InfrahubDatabase, # noqa: ARG002
23
+ account_session: AccountSession, # noqa: ARG002
24
24
  analyzed_query: InfrahubGraphQLQueryAnalyzer,
25
- query_parameters: GraphqlParams,
26
- branch: Branch,
25
+ query_parameters: GraphqlParams, # noqa: ARG002
26
+ branch: Branch, # noqa: ARG002
27
27
  ) -> CheckerResolution:
28
28
  if not self.anonymous_access_allowed_func() or analyzed_query.contains_mutation:
29
29
  raise AuthorizationError("Authentication is required to perform this operation")
@@ -25,16 +25,16 @@ class DefaultBranchPermissionChecker(GraphQLQueryPermissionCheckerInterface):
25
25
  "InfrahubAccountTokenDelete",
26
26
  ]
27
27
 
28
- async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool:
28
+ async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: # noqa: ARG002
29
29
  return config.SETTINGS.main.allow_anonymous_access or account_session.authenticated
30
30
 
31
31
  async def check(
32
32
  self,
33
- db: InfrahubDatabase,
34
- account_session: AccountSession,
33
+ db: InfrahubDatabase, # noqa: ARG002
34
+ account_session: AccountSession, # noqa: ARG002
35
35
  analyzed_query: InfrahubGraphQLQueryAnalyzer,
36
36
  query_parameters: GraphqlParams,
37
- branch: Branch,
37
+ branch: Branch, # noqa: ARG002
38
38
  ) -> CheckerResolution:
39
39
  operates_on_default_branch = analyzed_query.branch is None or analyzed_query.branch.name in (
40
40
  GLOBAL_BRANCH_NAME,
@@ -17,16 +17,16 @@ class MergeBranchPermissionChecker(GraphQLQueryPermissionCheckerInterface):
17
17
  action=GlobalPermissions.MERGE_BRANCH.value, decision=PermissionDecision.ALLOW_ALL.value
18
18
  )
19
19
 
20
- async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool:
20
+ async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: # noqa: ARG002
21
21
  return config.SETTINGS.main.allow_anonymous_access or account_session.authenticated
22
22
 
23
23
  async def check(
24
24
  self,
25
- db: InfrahubDatabase,
26
- account_session: AccountSession,
25
+ db: InfrahubDatabase, # noqa: ARG002
26
+ account_session: AccountSession, # noqa: ARG002
27
27
  analyzed_query: InfrahubGraphQLQueryAnalyzer,
28
28
  query_parameters: GraphqlParams,
29
- branch: Branch,
29
+ branch: Branch, # noqa: ARG002
30
30
  ) -> CheckerResolution:
31
31
  if "BranchMerge" in [operation.name for operation in analyzed_query.operations]:
32
32
  query_parameters.context.active_permissions.raise_for_permission(permission=self.permission_required)
@@ -18,16 +18,16 @@ from .interface import CheckerResolution, GraphQLQueryPermissionCheckerInterface
18
18
  class ObjectPermissionChecker(GraphQLQueryPermissionCheckerInterface):
19
19
  """Checker that makes sure a user account can perform some action on some kind of objects."""
20
20
 
21
- async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool:
21
+ async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: # noqa: ARG002
22
22
  return config.SETTINGS.main.allow_anonymous_access or account_session.authenticated
23
23
 
24
24
  async def check(
25
25
  self,
26
- db: InfrahubDatabase,
27
- account_session: AccountSession,
26
+ db: InfrahubDatabase, # noqa: ARG002
27
+ account_session: AccountSession, # noqa: ARG002
28
28
  analyzed_query: InfrahubGraphQLQueryAnalyzer,
29
29
  query_parameters: GraphqlParams,
30
- branch: Branch,
30
+ branch: Branch, # noqa: ARG002
31
31
  ) -> CheckerResolution:
32
32
  required_decision = (
33
33
  PermissionDecisionFlag.ALLOW_DEFAULT
@@ -36,34 +36,27 @@ class ObjectPermissionChecker(GraphQLQueryPermissionCheckerInterface):
36
36
  else PermissionDecisionFlag.ALLOW_OTHER
37
37
  )
38
38
 
39
- kinds = await analyzed_query.get_models_in_use(types=query_parameters.context.types)
40
-
41
- # Identify which operations are performed. As we don't have a mapping between kinds and the
42
- # operation we currently require permissions all defined permissions for all objects
43
- # within the GraphQL query / mutation
44
- actions: set[str] = set()
45
- for operation in analyzed_query.operations:
46
- for kind in kinds:
47
- if operation.name and operation.name.startswith(kind):
48
- # An empty string after prefix removal means a query to "view"
49
- query_action = operation.name[len(kind) :].lower() or "view"
50
- if query_action == "upsert":
51
- # Require both create and update for Upsert mutations
52
- actions.add("create")
53
- actions.add("update")
54
- else:
55
- actions.add(query_action)
56
-
57
- # Infer required permissions from the kind/operation map
58
39
  permissions: list[ObjectPermission] = []
59
- for action in actions:
60
- for kind in kinds:
40
+ for kind, object_access in analyzed_query.query_report.requested_read.items():
41
+ if object_access.attributes or object_access.relationships:
42
+ extracted_words = extract_camelcase_words(kind)
43
+ permissions.append(
44
+ ObjectPermission(
45
+ namespace=extracted_words[0],
46
+ name="".join(extracted_words[1:]),
47
+ action="view",
48
+ decision=required_decision,
49
+ )
50
+ )
51
+
52
+ for kind, requested_permissions in analyzed_query.query_report.kind_action_map.items():
53
+ for requested_permission in requested_permissions:
61
54
  extracted_words = extract_camelcase_words(kind)
62
55
  permissions.append(
63
56
  ObjectPermission(
64
57
  namespace=extracted_words[0],
65
58
  name="".join(extracted_words[1:]),
66
- action=action.lower(),
59
+ action=requested_permission.value,
67
60
  decision=required_decision,
68
61
  )
69
62
  )
@@ -83,19 +76,19 @@ class AccountManagerPermissionChecker(GraphQLQueryPermissionCheckerInterface):
83
76
  action=GlobalPermissions.MANAGE_ACCOUNTS.value, decision=PermissionDecision.ALLOW_ALL.value
84
77
  )
85
78
 
86
- async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool:
79
+ async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: # noqa: ARG002
87
80
  return config.SETTINGS.main.allow_anonymous_access or account_session.authenticated
88
81
 
89
82
  async def check(
90
83
  self,
91
84
  db: InfrahubDatabase,
92
- account_session: AccountSession,
85
+ account_session: AccountSession, # noqa: ARG002
93
86
  analyzed_query: InfrahubGraphQLQueryAnalyzer,
94
87
  query_parameters: GraphqlParams,
95
88
  branch: Branch,
96
89
  ) -> CheckerResolution:
97
90
  is_account_operation = False
98
- kinds = await analyzed_query.get_models_in_use(types=query_parameters.context.types)
91
+ kinds = analyzed_query.query_report.impacted_models
99
92
  operation_names = [operation.name for operation in analyzed_query.operations]
100
93
 
101
94
  for kind in kinds:
@@ -127,19 +120,19 @@ class PermissionManagerPermissionChecker(GraphQLQueryPermissionCheckerInterface)
127
120
  action=GlobalPermissions.MANAGE_PERMISSIONS.value, decision=PermissionDecision.ALLOW_ALL.value
128
121
  )
129
122
 
130
- async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool:
123
+ async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: # noqa: ARG002
131
124
  return config.SETTINGS.main.allow_anonymous_access or account_session.authenticated
132
125
 
133
126
  async def check(
134
127
  self,
135
128
  db: InfrahubDatabase,
136
- account_session: AccountSession,
129
+ account_session: AccountSession, # noqa: ARG002
137
130
  analyzed_query: InfrahubGraphQLQueryAnalyzer,
138
131
  query_parameters: GraphqlParams,
139
132
  branch: Branch,
140
133
  ) -> CheckerResolution:
141
134
  is_permission_operation = False
142
- kinds = await analyzed_query.get_models_in_use(types=query_parameters.context.types)
135
+ kinds = analyzed_query.query_report.impacted_models
143
136
 
144
137
  for kind in kinds:
145
138
  schema = get_schema(db=db, branch=branch, node_schema=kind)
@@ -168,19 +161,19 @@ class RepositoryManagerPermissionChecker(GraphQLQueryPermissionCheckerInterface)
168
161
  action=GlobalPermissions.MANAGE_REPOSITORIES.value, decision=PermissionDecision.ALLOW_ALL.value
169
162
  )
170
163
 
171
- async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool:
164
+ async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: # noqa: ARG002
172
165
  return config.SETTINGS.main.allow_anonymous_access or account_session.authenticated
173
166
 
174
167
  async def check(
175
168
  self,
176
169
  db: InfrahubDatabase,
177
- account_session: AccountSession,
170
+ account_session: AccountSession, # noqa: ARG002
178
171
  analyzed_query: InfrahubGraphQLQueryAnalyzer,
179
172
  query_parameters: GraphqlParams,
180
173
  branch: Branch,
181
174
  ) -> CheckerResolution:
182
175
  is_repository_operation = False
183
- kinds = await analyzed_query.get_models_in_use(types=query_parameters.context.types)
176
+ kinds = analyzed_query.query_report.impacted_models
184
177
 
185
178
  for kind in kinds:
186
179
  schema = get_schema(db=db, branch=branch, node_schema=kind)
@@ -17,16 +17,16 @@ class SuperAdminPermissionChecker(GraphQLQueryPermissionCheckerInterface):
17
17
  action=GlobalPermissions.SUPER_ADMIN.value, decision=PermissionDecision.ALLOW_ALL.value
18
18
  )
19
19
 
20
- async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool:
20
+ async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: # noqa: ARG002
21
21
  return config.SETTINGS.main.allow_anonymous_access or account_session.authenticated
22
22
 
23
23
  async def check(
24
24
  self,
25
- db: InfrahubDatabase,
26
- account_session: AccountSession,
27
- analyzed_query: InfrahubGraphQLQueryAnalyzer,
25
+ db: InfrahubDatabase, # noqa: ARG002
26
+ account_session: AccountSession, # noqa: ARG002
27
+ analyzed_query: InfrahubGraphQLQueryAnalyzer, # noqa: ARG002
28
28
  query_parameters: GraphqlParams,
29
- branch: Branch,
29
+ branch: Branch, # noqa: ARG002
30
30
  ) -> CheckerResolution:
31
31
  return (
32
32
  CheckerResolution.TERMINATE
infrahub/graphql/enums.py CHANGED
@@ -6,7 +6,7 @@ import graphene
6
6
  from infrahub.core.enums import generate_python_enum
7
7
  from infrahub.core.schema import AttributeSchema, MainSchemaTypes
8
8
 
9
- ENUM_NAME_REGEX = re.compile("[_a-zA-Z0-9]+")
9
+ ENUM_NAME_REGEX = re.compile(r"[_a-zA-Z0-9]+")
10
10
 
11
11
 
12
12
  def get_enum_attribute_type_name(node_schema: MainSchemaTypes, attr_schema: AttributeSchema) -> str:
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING
5
5
 
6
6
  from starlette.background import BackgroundTasks
7
7
 
8
+ from infrahub.context import InfrahubContext
8
9
  from infrahub.core import registry
9
10
  from infrahub.core.timestamp import Timestamp
10
11
  from infrahub.exceptions import InitializationError
@@ -35,9 +36,9 @@ class GraphqlContext:
35
36
  branch: Branch
36
37
  types: dict
37
38
  single_relationship_resolver: SingleRelationshipResolver
39
+ service: InfrahubServices | None = None
38
40
  at: Timestamp | None = None
39
41
  related_node_ids: set | None = None
40
- service: InfrahubServices | None = None
41
42
  account_session: AccountSession | None = None
42
43
  permissions: PermissionManager | None = None
43
44
  background: BackgroundTasks | None = None
@@ -70,6 +71,9 @@ class GraphqlContext:
70
71
  return self.service
71
72
  raise InitializationError("GraphQLContext doesn't contain a service")
72
73
 
74
+ def get_context(self) -> InfrahubContext:
75
+ return InfrahubContext.init(branch=self.branch, account=self.active_account_session)
76
+
73
77
 
74
78
  async def prepare_graphql_params(
75
79
  db: InfrahubDatabase,
@@ -50,7 +50,7 @@ class NodeDataLoader(DataLoader[str, Node | None]):
50
50
  self.query_params = query_params
51
51
  self.db = db
52
52
 
53
- async def batch_load_fn(self, keys: list[Any]) -> list[Node | None]: # pylint: disable=method-hidden
53
+ async def batch_load_fn(self, keys: list[Any]) -> list[Node | None]:
54
54
  async with self.db.start_session() as db:
55
55
  nodes_by_id = await NodeManager.get_many(
56
56
  db=db,
@@ -75,7 +75,7 @@ def to_frozen_set(to_freeze: dict[str, Any]) -> frozenset:
75
75
  for k, v in to_freeze.items():
76
76
  if isinstance(v, dict):
77
77
  freezing_dict[k] = to_frozen_set(v)
78
- elif isinstance(v, (list, set)):
78
+ elif isinstance(v, list | set):
79
79
  freezing_dict[k] = frozenset(v)
80
80
  else:
81
81
  freezing_dict[k] = v