openmetadata-ingestion 1.3.0.1__py3-none-any.whl → 1.3.1.1__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.

Potentially problematic release.


This version of openmetadata-ingestion might be problematic. Click here for more details.

Files changed (576) hide show
  1. metadata/cli/db_dump.py +1 -0
  2. metadata/data_insight/processor/reports/cost_analysis_report_data_processor.py +39 -47
  3. metadata/data_insight/processor/reports/data_processor.py +1 -0
  4. metadata/data_insight/producer/cost_analysis_producer.py +78 -14
  5. metadata/data_insight/producer/entity_producer.py +1 -1
  6. metadata/data_insight/producer/producer_interface.py +1 -1
  7. metadata/data_insight/producer/web_analytics_producer.py +1 -1
  8. metadata/data_insight/source/metadata.py +10 -1
  9. metadata/data_quality/validations/table/base/tableColumnToMatchSet.py +2 -1
  10. metadata/data_quality/validations/table/pandas/tableColumnToMatchSet.py +2 -1
  11. metadata/data_quality/validations/table/sqlalchemy/tableColumnToMatchSet.py +7 -2
  12. metadata/examples/workflows/bigtable.yaml +32 -0
  13. metadata/generated/antlr/EntityLinkLexer.py +353 -319
  14. metadata/generated/schema/analytics/__init__.py +1 -1
  15. metadata/generated/schema/analytics/basic.py +1 -1
  16. metadata/generated/schema/analytics/reportData.py +1 -1
  17. metadata/generated/schema/analytics/reportDataType/__init__.py +1 -1
  18. metadata/generated/schema/analytics/reportDataType/aggregatedCostAnalysisReportData.py +1 -1
  19. metadata/generated/schema/analytics/reportDataType/entityReportData.py +1 -1
  20. metadata/generated/schema/analytics/reportDataType/rawCostAnalysisReportData.py +1 -1
  21. metadata/generated/schema/analytics/reportDataType/webAnalyticEntityViewReportData.py +1 -1
  22. metadata/generated/schema/analytics/reportDataType/webAnalyticUserActivityReportData.py +1 -1
  23. metadata/generated/schema/analytics/webAnalyticEvent.py +1 -1
  24. metadata/generated/schema/analytics/webAnalyticEventData.py +1 -1
  25. metadata/generated/schema/analytics/webAnalyticEventType/__init__.py +1 -1
  26. metadata/generated/schema/analytics/webAnalyticEventType/customEvent.py +1 -1
  27. metadata/generated/schema/analytics/webAnalyticEventType/pageViewEvent.py +1 -1
  28. metadata/generated/schema/api/__init__.py +1 -1
  29. metadata/generated/schema/api/addGlossaryToAssetsRequest.py +1 -1
  30. metadata/generated/schema/api/analytics/__init__.py +1 -1
  31. metadata/generated/schema/api/analytics/createWebAnalyticEvent.py +1 -1
  32. metadata/generated/schema/api/automations/__init__.py +1 -1
  33. metadata/generated/schema/api/automations/createWorkflow.py +1 -1
  34. metadata/generated/schema/api/bulkAssets.py +1 -1
  35. metadata/generated/schema/api/classification/__init__.py +1 -1
  36. metadata/generated/schema/api/classification/createClassification.py +1 -1
  37. metadata/generated/schema/api/classification/createTag.py +1 -1
  38. metadata/generated/schema/api/classification/loadTags.py +1 -1
  39. metadata/generated/schema/api/createBot.py +1 -1
  40. metadata/generated/schema/api/createEventPublisherJob.py +2 -2
  41. metadata/generated/schema/api/createType.py +1 -1
  42. metadata/generated/schema/api/data/__init__.py +1 -1
  43. metadata/generated/schema/api/data/createChart.py +1 -1
  44. metadata/generated/schema/api/data/createContainer.py +1 -1
  45. metadata/generated/schema/api/data/createCustomProperty.py +6 -10
  46. metadata/generated/schema/api/data/createDashboard.py +1 -1
  47. metadata/generated/schema/api/data/createDashboardDataModel.py +1 -1
  48. metadata/generated/schema/api/data/createDatabase.py +1 -1
  49. metadata/generated/schema/api/data/createDatabaseSchema.py +1 -1
  50. metadata/generated/schema/api/data/createGlossary.py +1 -1
  51. metadata/generated/schema/api/data/createGlossaryTerm.py +1 -1
  52. metadata/generated/schema/api/data/createMlModel.py +1 -1
  53. metadata/generated/schema/api/data/createPipeline.py +1 -1
  54. metadata/generated/schema/api/data/createQuery.py +1 -1
  55. metadata/generated/schema/api/data/createSearchIndex.py +1 -1
  56. metadata/generated/schema/api/data/createStoredProcedure.py +1 -1
  57. metadata/generated/schema/api/data/createTable.py +1 -1
  58. metadata/generated/schema/api/data/createTableProfile.py +1 -1
  59. metadata/generated/schema/api/data/createTopic.py +1 -1
  60. metadata/generated/schema/api/data/loadGlossary.py +1 -1
  61. metadata/generated/schema/api/data/restoreEntity.py +1 -1
  62. metadata/generated/schema/api/dataInsight/__init__.py +1 -1
  63. metadata/generated/schema/api/dataInsight/createDataInsightChart.py +1 -1
  64. metadata/generated/schema/api/dataInsight/kpi/__init__.py +1 -1
  65. metadata/generated/schema/api/dataInsight/kpi/createKpiRequest.py +1 -1
  66. metadata/generated/schema/api/docStore/__init__.py +1 -1
  67. metadata/generated/schema/api/docStore/createDocument.py +1 -1
  68. metadata/generated/schema/api/domains/__init__.py +1 -1
  69. metadata/generated/schema/api/domains/createDataProduct.py +1 -1
  70. metadata/generated/schema/api/domains/createDomain.py +1 -1
  71. metadata/generated/schema/api/feed/__init__.py +1 -1
  72. metadata/generated/schema/api/feed/closeTask.py +1 -1
  73. metadata/generated/schema/api/feed/createPost.py +1 -1
  74. metadata/generated/schema/api/feed/createSuggestion.py +1 -1
  75. metadata/generated/schema/api/feed/createThread.py +1 -1
  76. metadata/generated/schema/api/feed/resolveTask.py +1 -1
  77. metadata/generated/schema/api/feed/threadCount.py +1 -1
  78. metadata/generated/schema/api/lineage/__init__.py +1 -1
  79. metadata/generated/schema/api/lineage/addLineage.py +1 -1
  80. metadata/generated/schema/api/openMetadataServerVersion.py +1 -1
  81. metadata/generated/schema/api/policies/__init__.py +1 -1
  82. metadata/generated/schema/api/policies/createPolicy.py +1 -1
  83. metadata/generated/schema/api/services/__init__.py +1 -1
  84. metadata/generated/schema/api/services/createDashboardService.py +1 -1
  85. metadata/generated/schema/api/services/createDatabaseService.py +1 -1
  86. metadata/generated/schema/api/services/createMessagingService.py +1 -1
  87. metadata/generated/schema/api/services/createMetadataService.py +1 -1
  88. metadata/generated/schema/api/services/createMlModelService.py +1 -1
  89. metadata/generated/schema/api/services/createPipelineService.py +1 -1
  90. metadata/generated/schema/api/services/createSearchService.py +1 -1
  91. metadata/generated/schema/api/services/createStorageService.py +1 -1
  92. metadata/generated/schema/api/services/ingestionPipelines/__init__.py +1 -1
  93. metadata/generated/schema/api/services/ingestionPipelines/createIngestionPipeline.py +1 -1
  94. metadata/generated/schema/api/setOwner.py +1 -1
  95. metadata/generated/schema/api/teams/__init__.py +1 -1
  96. metadata/generated/schema/api/teams/createPersona.py +1 -1
  97. metadata/generated/schema/api/teams/createRole.py +1 -1
  98. metadata/generated/schema/api/teams/createTeam.py +1 -1
  99. metadata/generated/schema/api/teams/createUser.py +1 -1
  100. metadata/generated/schema/api/tests/__init__.py +1 -1
  101. metadata/generated/schema/api/tests/createCustomMetric.py +1 -1
  102. metadata/generated/schema/api/tests/createLogicalTestCases.py +1 -1
  103. metadata/generated/schema/api/tests/createTestCase.py +1 -1
  104. metadata/generated/schema/api/tests/createTestCaseResolutionStatus.py +1 -1
  105. metadata/generated/schema/api/tests/createTestDefinition.py +2 -1
  106. metadata/generated/schema/api/tests/createTestSuite.py +1 -1
  107. metadata/generated/schema/api/voteRequest.py +1 -1
  108. metadata/generated/schema/auth/__init__.py +1 -1
  109. metadata/generated/schema/auth/basicAuth.py +1 -1
  110. metadata/generated/schema/auth/basicLoginRequest.py +1 -1
  111. metadata/generated/schema/auth/changePasswordRequest.py +1 -1
  112. metadata/generated/schema/auth/createPersonalToken.py +1 -1
  113. metadata/generated/schema/auth/emailRequest.py +1 -1
  114. metadata/generated/schema/auth/emailVerificationToken.py +1 -1
  115. metadata/generated/schema/auth/generateToken.py +1 -1
  116. metadata/generated/schema/auth/jwtAuth.py +1 -1
  117. metadata/generated/schema/auth/loginRequest.py +1 -1
  118. metadata/generated/schema/auth/logoutRequest.py +1 -1
  119. metadata/generated/schema/auth/passwordResetRequest.py +1 -1
  120. metadata/generated/schema/auth/passwordResetToken.py +1 -1
  121. metadata/generated/schema/auth/personalAccessToken.py +1 -1
  122. metadata/generated/schema/auth/refreshToken.py +1 -1
  123. metadata/generated/schema/auth/registrationRequest.py +1 -1
  124. metadata/generated/schema/auth/revokePersonalToken.py +1 -1
  125. metadata/generated/schema/auth/revokeToken.py +1 -1
  126. metadata/generated/schema/auth/serviceTokenEnum.py +1 -1
  127. metadata/generated/schema/auth/ssoAuth.py +1 -1
  128. metadata/generated/schema/auth/tokenRefreshRequest.py +1 -1
  129. metadata/generated/schema/configuration/__init__.py +1 -1
  130. metadata/generated/schema/configuration/appsPrivateConfiguration.py +1 -1
  131. metadata/generated/schema/configuration/authConfig.py +1 -1
  132. metadata/generated/schema/configuration/authenticationConfiguration.py +1 -1
  133. metadata/generated/schema/configuration/authorizerConfiguration.py +1 -1
  134. metadata/generated/schema/configuration/changeEventConfiguration.py +1 -1
  135. metadata/generated/schema/configuration/dataQualityConfiguration.py +16 -0
  136. metadata/generated/schema/configuration/elasticSearchConfiguration.py +1 -1
  137. metadata/generated/schema/configuration/eventHandlerConfiguration.py +1 -1
  138. metadata/generated/schema/configuration/fernetConfiguration.py +1 -1
  139. metadata/generated/schema/configuration/jwtTokenConfiguration.py +1 -1
  140. metadata/generated/schema/configuration/kafkaEventConfiguration.py +1 -1
  141. metadata/generated/schema/configuration/ldapConfiguration.py +1 -1
  142. metadata/generated/schema/configuration/ldapTrustStoreConfig/__init__.py +1 -1
  143. metadata/generated/schema/configuration/ldapTrustStoreConfig/customTrustManagerConfig.py +1 -1
  144. metadata/generated/schema/configuration/ldapTrustStoreConfig/hostNameConfig.py +1 -1
  145. metadata/generated/schema/configuration/ldapTrustStoreConfig/jvmDefaultConfig.py +1 -1
  146. metadata/generated/schema/configuration/ldapTrustStoreConfig/trustAllConfig.py +1 -1
  147. metadata/generated/schema/configuration/ldapTrustStoreConfig/truststoreConfig.py +1 -1
  148. metadata/generated/schema/configuration/loginConfiguration.py +1 -1
  149. metadata/generated/schema/configuration/logoConfiguration.py +1 -1
  150. metadata/generated/schema/configuration/pipelineServiceClientConfiguration.py +1 -1
  151. metadata/generated/schema/configuration/slackAppConfiguration.py +1 -1
  152. metadata/generated/schema/configuration/taskNotificationConfiguration.py +1 -1
  153. metadata/generated/schema/configuration/testResultNotificationConfiguration.py +1 -1
  154. metadata/generated/schema/dataInsight/__init__.py +1 -1
  155. metadata/generated/schema/dataInsight/dataInsightChart.py +1 -1
  156. metadata/generated/schema/dataInsight/dataInsightChartResult.py +1 -1
  157. metadata/generated/schema/dataInsight/kpi/__init__.py +1 -1
  158. metadata/generated/schema/dataInsight/kpi/basic.py +1 -1
  159. metadata/generated/schema/dataInsight/kpi/kpi.py +1 -1
  160. metadata/generated/schema/dataInsight/type/__init__.py +1 -1
  161. metadata/generated/schema/dataInsight/type/aggregatedUnusedAssetsCount.py +1 -1
  162. metadata/generated/schema/dataInsight/type/aggregatedUnusedAssetsSize.py +1 -1
  163. metadata/generated/schema/dataInsight/type/aggregatedUsedVsUnusedAssetsCount.py +1 -1
  164. metadata/generated/schema/dataInsight/type/aggregatedUsedVsUnusedAssetsSize.py +1 -1
  165. metadata/generated/schema/dataInsight/type/dailyActiveUsers.py +1 -1
  166. metadata/generated/schema/dataInsight/type/mostActiveUsers.py +1 -1
  167. metadata/generated/schema/dataInsight/type/mostViewedEntities.py +1 -1
  168. metadata/generated/schema/dataInsight/type/pageViewsByEntities.py +1 -1
  169. metadata/generated/schema/dataInsight/type/percentageOfEntitiesWithDescriptionByType.py +1 -1
  170. metadata/generated/schema/dataInsight/type/percentageOfEntitiesWithOwnerByType.py +1 -1
  171. metadata/generated/schema/dataInsight/type/percentageOfServicesWithDescription.py +1 -1
  172. metadata/generated/schema/dataInsight/type/percentageOfServicesWithOwner.py +1 -1
  173. metadata/generated/schema/dataInsight/type/totalEntitiesByTier.py +1 -1
  174. metadata/generated/schema/dataInsight/type/totalEntitiesByType.py +1 -1
  175. metadata/generated/schema/dataInsight/type/unusedAssets.py +1 -1
  176. metadata/generated/schema/email/__init__.py +1 -1
  177. metadata/generated/schema/email/emailRequest.py +1 -1
  178. metadata/generated/schema/email/smtpSettings.py +1 -1
  179. metadata/generated/schema/entity/__init__.py +1 -1
  180. metadata/generated/schema/entity/applications/__init__.py +1 -1
  181. metadata/generated/schema/entity/applications/app.py +1 -1
  182. metadata/generated/schema/entity/applications/appRunRecord.py +1 -1
  183. metadata/generated/schema/entity/applications/configuration/__init__.py +1 -1
  184. metadata/generated/schema/entity/applications/configuration/applicationConfig.py +1 -1
  185. metadata/generated/schema/entity/applications/configuration/external/__init__.py +1 -1
  186. metadata/generated/schema/entity/applications/configuration/external/autoTaggerAppConfig.py +1 -1
  187. metadata/generated/schema/entity/applications/configuration/external/metaPilotAppConfig.py +1 -1
  188. metadata/generated/schema/entity/applications/configuration/internal/__init__.py +1 -1
  189. metadata/generated/schema/entity/applications/configuration/internal/dataInsightsAppConfig.py +1 -1
  190. metadata/generated/schema/entity/applications/configuration/internal/dataInsightsReportAppConfig.py +1 -1
  191. metadata/generated/schema/entity/applications/configuration/internal/searchIndexingAppConfig.py +2 -2
  192. metadata/generated/schema/entity/applications/configuration/private/external/__init__.py +1 -1
  193. metadata/generated/schema/entity/applications/configuration/private/external/metaPilotAppPrivateConfig.py +1 -1
  194. metadata/generated/schema/entity/applications/createAppRequest.py +1 -1
  195. metadata/generated/schema/entity/applications/jobStatus.py +1 -1
  196. metadata/generated/schema/entity/applications/liveExecutionContext.py +1 -1
  197. metadata/generated/schema/entity/applications/marketplace/__init__.py +1 -1
  198. metadata/generated/schema/entity/applications/marketplace/appMarketPlaceDefinition.py +1 -1
  199. metadata/generated/schema/entity/applications/marketplace/createAppMarketPlaceDefinitionReq.py +1 -1
  200. metadata/generated/schema/entity/applications/scheduledExecutionContext.py +1 -1
  201. metadata/generated/schema/entity/automations/__init__.py +1 -1
  202. metadata/generated/schema/entity/automations/testServiceConnection.py +1 -1
  203. metadata/generated/schema/entity/automations/workflow.py +1 -1
  204. metadata/generated/schema/entity/bot.py +1 -1
  205. metadata/generated/schema/entity/classification/__init__.py +1 -1
  206. metadata/generated/schema/entity/classification/classification.py +1 -1
  207. metadata/generated/schema/entity/classification/tag.py +1 -1
  208. metadata/generated/schema/entity/data/__init__.py +1 -1
  209. metadata/generated/schema/entity/data/chart.py +1 -1
  210. metadata/generated/schema/entity/data/container.py +1 -1
  211. metadata/generated/schema/entity/data/dashboard.py +1 -1
  212. metadata/generated/schema/entity/data/dashboardDataModel.py +1 -1
  213. metadata/generated/schema/entity/data/database.py +1 -1
  214. metadata/generated/schema/entity/data/databaseSchema.py +1 -1
  215. metadata/generated/schema/entity/data/glossary.py +1 -1
  216. metadata/generated/schema/entity/data/glossaryTerm.py +1 -1
  217. metadata/generated/schema/entity/data/metrics.py +1 -1
  218. metadata/generated/schema/entity/data/mlmodel.py +2 -2
  219. metadata/generated/schema/entity/data/pipeline.py +1 -1
  220. metadata/generated/schema/entity/data/query.py +1 -1
  221. metadata/generated/schema/entity/data/report.py +1 -1
  222. metadata/generated/schema/entity/data/searchIndex.py +1 -1
  223. metadata/generated/schema/entity/data/storedProcedure.py +1 -1
  224. metadata/generated/schema/entity/data/table.py +1 -1
  225. metadata/generated/schema/entity/data/topic.py +1 -1
  226. metadata/generated/schema/entity/docStore/__init__.py +1 -1
  227. metadata/generated/schema/entity/docStore/document.py +1 -1
  228. metadata/generated/schema/entity/domains/__init__.py +1 -1
  229. metadata/generated/schema/entity/domains/dataProduct.py +1 -1
  230. metadata/generated/schema/entity/domains/domain.py +1 -1
  231. metadata/generated/schema/entity/events/__init__.py +1 -1
  232. metadata/generated/schema/entity/events/webhook.py +1 -1
  233. metadata/generated/schema/entity/feed/__init__.py +1 -1
  234. metadata/generated/schema/entity/feed/suggestion.py +1 -1
  235. metadata/generated/schema/entity/feed/thread.py +1 -1
  236. metadata/generated/schema/entity/policies/__init__.py +1 -1
  237. metadata/generated/schema/entity/policies/accessControl/__init__.py +1 -1
  238. metadata/generated/schema/entity/policies/accessControl/resourceDescriptor.py +1 -1
  239. metadata/generated/schema/entity/policies/accessControl/resourcePermission.py +1 -1
  240. metadata/generated/schema/entity/policies/accessControl/rule.py +1 -1
  241. metadata/generated/schema/entity/policies/filters.py +1 -1
  242. metadata/generated/schema/entity/policies/policy.py +1 -1
  243. metadata/generated/schema/entity/services/__init__.py +1 -1
  244. metadata/generated/schema/entity/services/connections/__init__.py +1 -1
  245. metadata/generated/schema/entity/services/connections/common/__init__.py +1 -1
  246. metadata/generated/schema/entity/services/connections/common/sslCertPaths.py +1 -1
  247. metadata/generated/schema/entity/services/connections/common/sslCertValues.py +1 -1
  248. metadata/generated/schema/entity/services/connections/common/sslConfig.py +1 -1
  249. metadata/generated/schema/entity/services/connections/connectionBasicType.py +1 -1
  250. metadata/generated/schema/entity/services/connections/dashboard/__init__.py +1 -1
  251. metadata/generated/schema/entity/services/connections/dashboard/customDashboardConnection.py +1 -1
  252. metadata/generated/schema/entity/services/connections/dashboard/domoDashboardConnection.py +1 -1
  253. metadata/generated/schema/entity/services/connections/dashboard/lightdashConnection.py +1 -1
  254. metadata/generated/schema/entity/services/connections/dashboard/lookerConnection.py +1 -1
  255. metadata/generated/schema/entity/services/connections/dashboard/metabaseConnection.py +1 -1
  256. metadata/generated/schema/entity/services/connections/dashboard/modeConnection.py +1 -1
  257. metadata/generated/schema/entity/services/connections/dashboard/mstrConnection.py +1 -1
  258. metadata/generated/schema/entity/services/connections/dashboard/powerBIConnection.py +1 -1
  259. metadata/generated/schema/entity/services/connections/dashboard/qlikSenseConnection.py +1 -1
  260. metadata/generated/schema/entity/services/connections/dashboard/quickSightConnection.py +1 -1
  261. metadata/generated/schema/entity/services/connections/dashboard/redashConnection.py +1 -1
  262. metadata/generated/schema/entity/services/connections/dashboard/supersetConnection.py +1 -1
  263. metadata/generated/schema/entity/services/connections/dashboard/tableauConnection.py +1 -1
  264. metadata/generated/schema/entity/services/connections/database/__init__.py +1 -1
  265. metadata/generated/schema/entity/services/connections/database/athenaConnection.py +1 -1
  266. metadata/generated/schema/entity/services/connections/database/azureSQLConnection.py +1 -1
  267. metadata/generated/schema/entity/services/connections/database/bigQueryConnection.py +1 -1
  268. metadata/generated/schema/entity/services/connections/database/bigTableConnection.py +41 -0
  269. metadata/generated/schema/entity/services/connections/database/clickhouseConnection.py +1 -1
  270. metadata/generated/schema/entity/services/connections/database/common/__init__.py +1 -1
  271. metadata/generated/schema/entity/services/connections/database/common/basicAuth.py +1 -1
  272. metadata/generated/schema/entity/services/connections/database/common/iamAuthConfig.py +1 -1
  273. metadata/generated/schema/entity/services/connections/database/common/jwtAuth.py +1 -1
  274. metadata/generated/schema/entity/services/connections/database/couchbaseConnection.py +1 -1
  275. metadata/generated/schema/entity/services/connections/database/customDatabaseConnection.py +1 -1
  276. metadata/generated/schema/entity/services/connections/database/databricksConnection.py +1 -1
  277. metadata/generated/schema/entity/services/connections/database/datalake/__init__.py +1 -1
  278. metadata/generated/schema/entity/services/connections/database/datalake/azureConfig.py +1 -1
  279. metadata/generated/schema/entity/services/connections/database/datalake/gcsConfig.py +1 -1
  280. metadata/generated/schema/entity/services/connections/database/datalake/s3Config.py +1 -1
  281. metadata/generated/schema/entity/services/connections/database/datalakeConnection.py +1 -1
  282. metadata/generated/schema/entity/services/connections/database/db2Connection.py +1 -1
  283. metadata/generated/schema/entity/services/connections/database/deltaLakeConnection.py +1 -1
  284. metadata/generated/schema/entity/services/connections/database/domoDatabaseConnection.py +1 -1
  285. metadata/generated/schema/entity/services/connections/database/dorisConnection.py +1 -1
  286. metadata/generated/schema/entity/services/connections/database/druidConnection.py +1 -1
  287. metadata/generated/schema/entity/services/connections/database/dynamoDBConnection.py +1 -1
  288. metadata/generated/schema/entity/services/connections/database/glueConnection.py +1 -1
  289. metadata/generated/schema/entity/services/connections/database/greenplumConnection.py +1 -1
  290. metadata/generated/schema/entity/services/connections/database/hiveConnection.py +1 -1
  291. metadata/generated/schema/entity/services/connections/database/iceberg/__init__.py +1 -1
  292. metadata/generated/schema/entity/services/connections/database/iceberg/dynamoDbCatalogConnection.py +1 -1
  293. metadata/generated/schema/entity/services/connections/database/iceberg/glueCatalogConnection.py +1 -1
  294. metadata/generated/schema/entity/services/connections/database/iceberg/hiveCatalogConnection.py +1 -1
  295. metadata/generated/schema/entity/services/connections/database/iceberg/icebergCatalog.py +1 -1
  296. metadata/generated/schema/entity/services/connections/database/iceberg/icebergFileSystem.py +1 -1
  297. metadata/generated/schema/entity/services/connections/database/iceberg/restCatalogConnection.py +1 -1
  298. metadata/generated/schema/entity/services/connections/database/icebergConnection.py +1 -1
  299. metadata/generated/schema/entity/services/connections/database/impalaConnection.py +1 -1
  300. metadata/generated/schema/entity/services/connections/database/mariaDBConnection.py +1 -1
  301. metadata/generated/schema/entity/services/connections/database/mongoDBConnection.py +1 -1
  302. metadata/generated/schema/entity/services/connections/database/mssqlConnection.py +1 -1
  303. metadata/generated/schema/entity/services/connections/database/mysqlConnection.py +1 -1
  304. metadata/generated/schema/entity/services/connections/database/oracleConnection.py +1 -1
  305. metadata/generated/schema/entity/services/connections/database/pinotDBConnection.py +8 -6
  306. metadata/generated/schema/entity/services/connections/database/postgresConnection.py +1 -1
  307. metadata/generated/schema/entity/services/connections/database/prestoConnection.py +1 -1
  308. metadata/generated/schema/entity/services/connections/database/redshiftConnection.py +1 -1
  309. metadata/generated/schema/entity/services/connections/database/salesforceConnection.py +1 -1
  310. metadata/generated/schema/entity/services/connections/database/sapHanaConnection.py +1 -1
  311. metadata/generated/schema/entity/services/connections/database/sasConnection.py +1 -1
  312. metadata/generated/schema/entity/services/connections/database/singleStoreConnection.py +1 -1
  313. metadata/generated/schema/entity/services/connections/database/snowflakeConnection.py +1 -1
  314. metadata/generated/schema/entity/services/connections/database/sqliteConnection.py +1 -1
  315. metadata/generated/schema/entity/services/connections/database/trinoConnection.py +1 -1
  316. metadata/generated/schema/entity/services/connections/database/unityCatalogConnection.py +1 -1
  317. metadata/generated/schema/entity/services/connections/database/verticaConnection.py +1 -1
  318. metadata/generated/schema/entity/services/connections/messaging/__init__.py +1 -1
  319. metadata/generated/schema/entity/services/connections/messaging/customMessagingConnection.py +1 -1
  320. metadata/generated/schema/entity/services/connections/messaging/kafkaConnection.py +14 -2
  321. metadata/generated/schema/entity/services/connections/messaging/kinesisConnection.py +1 -1
  322. metadata/generated/schema/entity/services/connections/messaging/pulsarConnection.py +1 -1
  323. metadata/generated/schema/entity/services/connections/messaging/redpandaConnection.py +14 -2
  324. metadata/generated/schema/entity/services/connections/messaging/saslMechanismType.py +1 -1
  325. metadata/generated/schema/entity/services/connections/metadata/__init__.py +1 -1
  326. metadata/generated/schema/entity/services/connections/metadata/alationConnection.py +1 -1
  327. metadata/generated/schema/entity/services/connections/metadata/amundsenConnection.py +1 -1
  328. metadata/generated/schema/entity/services/connections/metadata/atlasConnection.py +1 -1
  329. metadata/generated/schema/entity/services/connections/metadata/metadataESConnection.py +2 -2
  330. metadata/generated/schema/entity/services/connections/metadata/openMetadataConnection.py +1 -1
  331. metadata/generated/schema/entity/services/connections/mlmodel/__init__.py +1 -1
  332. metadata/generated/schema/entity/services/connections/mlmodel/customMlModelConnection.py +1 -1
  333. metadata/generated/schema/entity/services/connections/mlmodel/mlflowConnection.py +1 -1
  334. metadata/generated/schema/entity/services/connections/mlmodel/sageMakerConnection.py +1 -1
  335. metadata/generated/schema/entity/services/connections/mlmodel/sklearnConnection.py +1 -1
  336. metadata/generated/schema/entity/services/connections/pipeline/__init__.py +1 -1
  337. metadata/generated/schema/entity/services/connections/pipeline/airbyteConnection.py +1 -1
  338. metadata/generated/schema/entity/services/connections/pipeline/airflowConnection.py +1 -1
  339. metadata/generated/schema/entity/services/connections/pipeline/backendConnection.py +1 -1
  340. metadata/generated/schema/entity/services/connections/pipeline/customPipelineConnection.py +1 -1
  341. metadata/generated/schema/entity/services/connections/pipeline/dagsterConnection.py +1 -1
  342. metadata/generated/schema/entity/services/connections/pipeline/databricksPipelineConnection.py +1 -1
  343. metadata/generated/schema/entity/services/connections/pipeline/domoPipelineConnection.py +1 -1
  344. metadata/generated/schema/entity/services/connections/pipeline/fivetranConnection.py +1 -1
  345. metadata/generated/schema/entity/services/connections/pipeline/gluePipelineConnection.py +1 -1
  346. metadata/generated/schema/entity/services/connections/pipeline/nifiConnection.py +1 -1
  347. metadata/generated/schema/entity/services/connections/pipeline/sparkConnection.py +1 -1
  348. metadata/generated/schema/entity/services/connections/pipeline/splineConnection.py +1 -1
  349. metadata/generated/schema/entity/services/connections/search/__init__.py +1 -1
  350. metadata/generated/schema/entity/services/connections/search/customSearchConnection.py +1 -1
  351. metadata/generated/schema/entity/services/connections/search/elasticSearch/__init__.py +1 -1
  352. metadata/generated/schema/entity/services/connections/search/elasticSearch/apiAuth.py +1 -1
  353. metadata/generated/schema/entity/services/connections/search/elasticSearch/basicAuth.py +1 -1
  354. metadata/generated/schema/entity/services/connections/search/elasticSearchConnection.py +1 -1
  355. metadata/generated/schema/entity/services/connections/search/openSearchConnection.py +1 -1
  356. metadata/generated/schema/entity/services/connections/serviceConnection.py +1 -1
  357. metadata/generated/schema/entity/services/connections/storage/__init__.py +1 -1
  358. metadata/generated/schema/entity/services/connections/storage/adlsConnection.py +1 -1
  359. metadata/generated/schema/entity/services/connections/storage/customStorageConnection.py +1 -1
  360. metadata/generated/schema/entity/services/connections/storage/gcsConnection.py +1 -1
  361. metadata/generated/schema/entity/services/connections/storage/s3Connection.py +1 -1
  362. metadata/generated/schema/entity/services/connections/testConnectionDefinition.py +1 -1
  363. metadata/generated/schema/entity/services/connections/testConnectionResult.py +1 -1
  364. metadata/generated/schema/entity/services/dashboardService.py +1 -1
  365. metadata/generated/schema/entity/services/databaseService.py +4 -1
  366. metadata/generated/schema/entity/services/ingestionPipelines/__init__.py +1 -1
  367. metadata/generated/schema/entity/services/ingestionPipelines/ingestionPipeline.py +1 -1
  368. metadata/generated/schema/entity/services/ingestionPipelines/pipelineServiceClientResponse.py +1 -1
  369. metadata/generated/schema/entity/services/ingestionPipelines/status.py +1 -1
  370. metadata/generated/schema/entity/services/messagingService.py +1 -1
  371. metadata/generated/schema/entity/services/metadataService.py +1 -1
  372. metadata/generated/schema/entity/services/mlmodelService.py +1 -1
  373. metadata/generated/schema/entity/services/pipelineService.py +1 -1
  374. metadata/generated/schema/entity/services/searchService.py +1 -1
  375. metadata/generated/schema/entity/services/serviceType.py +1 -1
  376. metadata/generated/schema/entity/services/storageService.py +1 -1
  377. metadata/generated/schema/entity/teams/__init__.py +1 -1
  378. metadata/generated/schema/entity/teams/persona.py +1 -1
  379. metadata/generated/schema/entity/teams/role.py +1 -1
  380. metadata/generated/schema/entity/teams/team.py +1 -1
  381. metadata/generated/schema/entity/teams/teamHierarchy.py +1 -1
  382. metadata/generated/schema/entity/teams/user.py +1 -1
  383. metadata/generated/schema/entity/type.py +3 -18
  384. metadata/generated/schema/entity/utils/__init__.py +1 -1
  385. metadata/generated/schema/entity/utils/entitiesCount.py +1 -1
  386. metadata/generated/schema/entity/utils/servicesCount.py +1 -1
  387. metadata/generated/schema/entity/utils/supersetApiConnection.py +1 -1
  388. metadata/generated/schema/events/__init__.py +1 -1
  389. metadata/generated/schema/events/alertMetrics.py +1 -1
  390. metadata/generated/schema/events/api/__init__.py +1 -1
  391. metadata/generated/schema/events/api/createEventSubscription.py +1 -1
  392. metadata/generated/schema/events/emailAlertConfig.py +1 -1
  393. metadata/generated/schema/events/eventFilterRule.py +1 -1
  394. metadata/generated/schema/events/eventSubscription.py +5 -3
  395. metadata/generated/schema/events/eventSubscriptionOffset.py +1 -1
  396. metadata/generated/schema/events/failedEvent.py +1 -1
  397. metadata/generated/schema/events/filterResourceDescriptor.py +1 -1
  398. metadata/generated/schema/events/subscriptionResourceDescriptor.py +1 -1
  399. metadata/generated/schema/metadataIngestion/__init__.py +1 -1
  400. metadata/generated/schema/metadataIngestion/application.py +1 -1
  401. metadata/generated/schema/metadataIngestion/applicationPipeline.py +1 -1
  402. metadata/generated/schema/metadataIngestion/dashboardServiceMetadataPipeline.py +1 -1
  403. metadata/generated/schema/metadataIngestion/dataInsightPipeline.py +1 -1
  404. metadata/generated/schema/metadataIngestion/databaseServiceMetadataPipeline.py +1 -1
  405. metadata/generated/schema/metadataIngestion/databaseServiceProfilerPipeline.py +1 -1
  406. metadata/generated/schema/metadataIngestion/databaseServiceQueryLineagePipeline.py +1 -1
  407. metadata/generated/schema/metadataIngestion/databaseServiceQueryUsagePipeline.py +1 -1
  408. metadata/generated/schema/metadataIngestion/dbtPipeline.py +1 -1
  409. metadata/generated/schema/metadataIngestion/dbtconfig/__init__.py +1 -1
  410. metadata/generated/schema/metadataIngestion/dbtconfig/dbtAzureConfig.py +1 -1
  411. metadata/generated/schema/metadataIngestion/dbtconfig/dbtBucketDetails.py +1 -1
  412. metadata/generated/schema/metadataIngestion/dbtconfig/dbtCloudConfig.py +1 -1
  413. metadata/generated/schema/metadataIngestion/dbtconfig/dbtGCSConfig.py +1 -1
  414. metadata/generated/schema/metadataIngestion/dbtconfig/dbtHttpConfig.py +1 -1
  415. metadata/generated/schema/metadataIngestion/dbtconfig/dbtLocalConfig.py +1 -1
  416. metadata/generated/schema/metadataIngestion/dbtconfig/dbtS3Config.py +1 -1
  417. metadata/generated/schema/metadataIngestion/messagingServiceMetadataPipeline.py +1 -1
  418. metadata/generated/schema/metadataIngestion/metadataToElasticSearchPipeline.py +1 -1
  419. metadata/generated/schema/metadataIngestion/mlmodelServiceMetadataPipeline.py +1 -1
  420. metadata/generated/schema/metadataIngestion/pipelineServiceMetadataPipeline.py +1 -1
  421. metadata/generated/schema/metadataIngestion/searchServiceMetadataPipeline.py +1 -1
  422. metadata/generated/schema/metadataIngestion/storage/__init__.py +1 -1
  423. metadata/generated/schema/metadataIngestion/storage/containerMetadataConfig.py +1 -1
  424. metadata/generated/schema/metadataIngestion/storage/manifestMetadataConfig.py +1 -1
  425. metadata/generated/schema/metadataIngestion/storage/storageBucketDetails.py +1 -1
  426. metadata/generated/schema/metadataIngestion/storage/storageMetadataADLSConfig.py +1 -1
  427. metadata/generated/schema/metadataIngestion/storage/storageMetadataGCSConfig.py +1 -1
  428. metadata/generated/schema/metadataIngestion/storage/storageMetadataHttpConfig.py +1 -1
  429. metadata/generated/schema/metadataIngestion/storage/storageMetadataLocalConfig.py +1 -1
  430. metadata/generated/schema/metadataIngestion/storage/storageMetadataS3Config.py +1 -1
  431. metadata/generated/schema/metadataIngestion/storageServiceMetadataPipeline.py +1 -1
  432. metadata/generated/schema/metadataIngestion/testSuitePipeline.py +1 -1
  433. metadata/generated/schema/metadataIngestion/workflow.py +1 -1
  434. metadata/generated/schema/monitoring/__init__.py +1 -1
  435. metadata/generated/schema/monitoring/eventMonitorProvider.py +1 -1
  436. metadata/generated/schema/security/__init__.py +1 -1
  437. metadata/generated/schema/security/client/__init__.py +1 -1
  438. metadata/generated/schema/security/client/auth0SSOClientConfig.py +1 -1
  439. metadata/generated/schema/security/client/azureSSOClientConfig.py +1 -1
  440. metadata/generated/schema/security/client/customOidcSSOClientConfig.py +1 -1
  441. metadata/generated/schema/security/client/googleSSOClientConfig.py +1 -1
  442. metadata/generated/schema/security/client/oktaSSOClientConfig.py +1 -1
  443. metadata/generated/schema/security/client/openMetadataJWTClientConfig.py +1 -1
  444. metadata/generated/schema/security/client/samlSSOClientConfig.py +1 -1
  445. metadata/generated/schema/security/credentials/__init__.py +1 -1
  446. metadata/generated/schema/security/credentials/accessTokenAuth.py +1 -1
  447. metadata/generated/schema/security/credentials/apiAccessTokenAuth.py +1 -1
  448. metadata/generated/schema/security/credentials/awsCredentials.py +1 -1
  449. metadata/generated/schema/security/credentials/azureCredentials.py +16 -9
  450. metadata/generated/schema/security/credentials/basicAuth.py +1 -1
  451. metadata/generated/schema/security/credentials/bitbucketCredentials.py +1 -1
  452. metadata/generated/schema/security/credentials/gcpCredentials.py +1 -1
  453. metadata/generated/schema/security/credentials/gcpValues.py +1 -1
  454. metadata/generated/schema/security/credentials/gitCredentials.py +1 -1
  455. metadata/generated/schema/security/credentials/githubCredentials.py +1 -1
  456. metadata/generated/schema/security/secrets/__init__.py +1 -1
  457. metadata/generated/schema/security/secrets/secretsManagerClientLoader.py +1 -1
  458. metadata/generated/schema/security/secrets/secretsManagerConfiguration.py +1 -1
  459. metadata/generated/schema/security/secrets/secretsManagerProvider.py +3 -1
  460. metadata/generated/schema/security/securityConfiguration.py +1 -1
  461. metadata/generated/schema/security/ssl/__init__.py +1 -1
  462. metadata/generated/schema/security/ssl/validateSSLClientConfig.py +1 -1
  463. metadata/generated/schema/security/ssl/verifySSLConfig.py +1 -1
  464. metadata/generated/schema/settings/__init__.py +1 -1
  465. metadata/generated/schema/settings/settings.py +1 -1
  466. metadata/generated/schema/system/__init__.py +1 -1
  467. metadata/generated/schema/system/entityError.py +1 -1
  468. metadata/generated/schema/system/eventPublisherJob.py +1 -1
  469. metadata/generated/schema/system/indexingError.py +1 -1
  470. metadata/generated/schema/system/ui/__init__.py +1 -1
  471. metadata/generated/schema/system/ui/knowledgePanel.py +1 -1
  472. metadata/generated/schema/system/ui/page.py +1 -1
  473. metadata/generated/schema/tests/__init__.py +1 -1
  474. metadata/generated/schema/tests/assigned.py +1 -1
  475. metadata/generated/schema/tests/basic.py +1 -1
  476. metadata/generated/schema/tests/customMetric.py +1 -1
  477. metadata/generated/schema/tests/resolved.py +1 -1
  478. metadata/generated/schema/tests/testCase.py +1 -1
  479. metadata/generated/schema/tests/testCaseResolutionStatus.py +1 -1
  480. metadata/generated/schema/tests/testDefinition.py +2 -1
  481. metadata/generated/schema/tests/testSuite.py +1 -1
  482. metadata/generated/schema/type/__init__.py +1 -1
  483. metadata/generated/schema/type/auditLog.py +1 -1
  484. metadata/generated/schema/type/basic.py +6 -2
  485. metadata/generated/schema/type/bulkOperationResult.py +1 -1
  486. metadata/generated/schema/type/changeEvent.py +1 -1
  487. metadata/generated/schema/type/changeEventType.py +1 -1
  488. metadata/generated/schema/type/collectionDescriptor.py +1 -1
  489. metadata/generated/schema/type/csvDocumentation.py +1 -1
  490. metadata/generated/schema/type/csvErrorType.py +1 -1
  491. metadata/generated/schema/type/csvFile.py +1 -1
  492. metadata/generated/schema/type/csvImportResult.py +1 -1
  493. metadata/generated/schema/type/customProperties/__init__.py +3 -0
  494. metadata/generated/schema/type/customProperties/enumConfig.py +17 -0
  495. metadata/generated/schema/type/customProperty.py +52 -0
  496. metadata/generated/schema/type/dailyCount.py +1 -1
  497. metadata/generated/schema/type/databaseConnectionConfig.py +1 -1
  498. metadata/generated/schema/type/entityHistory.py +1 -1
  499. metadata/generated/schema/type/entityLineage.py +1 -1
  500. metadata/generated/schema/type/entityReference.py +1 -1
  501. metadata/generated/schema/type/entityReferenceList.py +1 -1
  502. metadata/generated/schema/type/entityRelationship.py +1 -1
  503. metadata/generated/schema/type/entityUsage.py +1 -1
  504. metadata/generated/schema/type/filterPattern.py +1 -1
  505. metadata/generated/schema/type/function.py +1 -1
  506. metadata/generated/schema/type/include.py +1 -1
  507. metadata/generated/schema/type/jdbcConnection.py +1 -1
  508. metadata/generated/schema/type/lifeCycle.py +1 -1
  509. metadata/generated/schema/type/paging.py +1 -1
  510. metadata/generated/schema/type/profile.py +1 -1
  511. metadata/generated/schema/type/queryParserData.py +1 -1
  512. metadata/generated/schema/type/reaction.py +1 -1
  513. metadata/generated/schema/type/schedule.py +1 -1
  514. metadata/generated/schema/type/schema.py +1 -1
  515. metadata/generated/schema/type/tableQuery.py +1 -1
  516. metadata/generated/schema/type/tableUsageCount.py +1 -1
  517. metadata/generated/schema/type/tagLabel.py +1 -1
  518. metadata/generated/schema/type/usageDetails.py +1 -1
  519. metadata/generated/schema/type/usageRequest.py +1 -1
  520. metadata/generated/schema/type/votes.py +1 -1
  521. metadata/great_expectations/action.py +4 -15
  522. metadata/ingestion/api/steps.py +14 -1
  523. metadata/ingestion/api/topology_runner.py +4 -1
  524. metadata/ingestion/models/custom_properties.py +0 -1
  525. metadata/ingestion/models/patch_request.py +61 -9
  526. metadata/ingestion/ometa/client.py +6 -0
  527. metadata/ingestion/ometa/mixins/custom_property_mixin.py +11 -11
  528. metadata/ingestion/ometa/mixins/patch_mixin.py +2 -0
  529. metadata/ingestion/ometa/ometa_api.py +1 -1
  530. metadata/ingestion/sink/metadata_rest.py +4 -2
  531. metadata/ingestion/source/dashboard/looker/metadata.py +3 -4
  532. metadata/ingestion/source/dashboard/metabase/client.py +4 -0
  533. metadata/ingestion/source/dashboard/metabase/metadata.py +5 -4
  534. metadata/ingestion/source/dashboard/metabase/models.py +2 -2
  535. metadata/ingestion/source/dashboard/tableau/metadata.py +18 -0
  536. metadata/ingestion/source/database/bigquery/helper.py +68 -1
  537. metadata/ingestion/source/database/bigquery/metadata.py +12 -3
  538. metadata/ingestion/source/database/bigquery/queries.py +22 -0
  539. metadata/ingestion/source/database/bigtable/client.py +62 -0
  540. metadata/ingestion/source/database/bigtable/connection.py +116 -0
  541. metadata/ingestion/source/database/bigtable/metadata.py +224 -0
  542. metadata/ingestion/source/database/bigtable/models.py +60 -0
  543. metadata/ingestion/source/database/common_db_source.py +2 -2
  544. metadata/ingestion/source/database/common_nosql_source.py +19 -2
  545. metadata/ingestion/source/database/databricks/metadata.py +132 -46
  546. metadata/ingestion/source/database/databricks/queries.py +3 -4
  547. metadata/ingestion/source/database/dbt/metadata.py +16 -28
  548. metadata/ingestion/source/database/oracle/queries.py +2 -2
  549. metadata/ingestion/source/messaging/common_broker_source.py +9 -7
  550. metadata/ingestion/source/messaging/kafka/connection.py +45 -4
  551. metadata/ingestion/source/mlmodel/sagemaker/metadata.py +20 -8
  552. metadata/profiler/orm/functions/conn_test.py +1 -0
  553. metadata/profiler/orm/functions/sum.py +1 -0
  554. metadata/profiler/orm/registry.py +1 -0
  555. metadata/profiler/processor/core.py +2 -2
  556. metadata/utils/datalake/datalake_utils.py +7 -1
  557. metadata/utils/execution_time_tracker.py +199 -0
  558. metadata/utils/filters.py +4 -0
  559. metadata/utils/helpers.py +0 -51
  560. metadata/utils/secrets/aws_based_secrets_manager.py +67 -4
  561. metadata/utils/secrets/aws_secrets_manager.py +7 -2
  562. metadata/utils/secrets/aws_ssm_secrets_manager.py +7 -2
  563. metadata/utils/secrets/azure_kv_secrets_manager.py +148 -0
  564. metadata/utils/secrets/external_secrets_manager.py +25 -3
  565. metadata/utils/secrets/secrets_manager_factory.py +13 -30
  566. metadata/workflow/base.py +4 -0
  567. metadata/workflow/output_handler.py +22 -0
  568. {openmetadata_ingestion-1.3.0.1.dist-info → openmetadata_ingestion-1.3.1.1.dist-info}/METADATA +298 -289
  569. {openmetadata_ingestion-1.3.0.1.dist-info → openmetadata_ingestion-1.3.1.1.dist-info}/RECORD +575 -564
  570. {openmetadata_ingestion-1.3.0.1.dist-info → openmetadata_ingestion-1.3.1.1.dist-info}/WHEEL +1 -1
  571. metadata/utils/secrets/client/loader.py +0 -77
  572. /metadata/{utils/secrets/client → ingestion/source/database/bigtable}/__init__.py +0 -0
  573. /metadata/utils/secrets/{noop_secrets_manager.py → db_secrets_manager.py} +0 -0
  574. {openmetadata_ingestion-1.3.0.1.dist-info → openmetadata_ingestion-1.3.1.1.dist-info}/LICENSE +0 -0
  575. {openmetadata_ingestion-1.3.0.1.dist-info → openmetadata_ingestion-1.3.1.1.dist-info}/entry_points.txt +0 -0
  576. {openmetadata_ingestion-1.3.0.1.dist-info → openmetadata_ingestion-1.3.1.1.dist-info}/top_level.txt +0 -0
@@ -19,6 +19,7 @@ from pydantic import BaseModel
19
19
 
20
20
  from metadata.ingestion.api.models import Entity, T
21
21
  from metadata.ingestion.ometa.mixins.patch_mixin_utils import PatchOperation
22
+ from metadata.ingestion.ometa.utils import model_str
22
23
 
23
24
 
24
25
  class PatchRequest(BaseModel):
@@ -138,12 +139,15 @@ ALLOWED_COMMON_PATCH_FIELDS = {
138
139
 
139
140
  RESTRICT_UPDATE_LIST = ["description", "tags", "owner"]
140
141
 
142
+ ARRAY_ENTITY_FIELDS = ["columns", "tasks", "fields"]
143
+
141
144
 
142
145
  def build_patch(
143
146
  source: T,
144
147
  destination: T,
145
148
  allowed_fields: Optional[Dict] = None,
146
149
  restrict_update_fields: Optional[List] = None,
150
+ array_entity_fields: Optional[List] = None,
147
151
  ) -> Optional[jsonpatch.JsonPatch]:
148
152
  """
149
153
  Given an Entity type and Source entity and Destination entity,
@@ -163,6 +167,13 @@ def build_patch(
163
167
  source = _remove_change_description(source)
164
168
  destination = _remove_change_description(destination)
165
169
 
170
+ if array_entity_fields:
171
+ _sort_array_entity_fields(
172
+ source=source,
173
+ destination=destination,
174
+ array_entity_fields=array_entity_fields,
175
+ )
176
+
166
177
  # Get the difference between source and destination
167
178
  if allowed_fields:
168
179
  patch = jsonpatch.make_patch(
@@ -192,20 +203,61 @@ def build_patch(
192
203
  # for a user editable fields like descriptions, tags we only want to support "add" operation in patch
193
204
  # we will remove the other operations for replace, remove from here
194
205
  if restrict_update_fields:
195
- patch.patch = [
196
- patch_ops
197
- for patch_ops in patch.patch
206
+ patch_ops_list = []
207
+ for patch_ops in patch.patch or []:
198
208
  if _determine_restricted_operation(
199
- patch_ops=patch_ops,
200
- restrict_update_fields=restrict_update_fields,
201
- )
202
- ]
203
-
209
+ patch_ops=patch_ops, restrict_update_fields=restrict_update_fields
210
+ ):
211
+ if (
212
+ patch_ops.get("op") == PatchOperation.REPLACE.value
213
+ and patch_ops.get("value") is None
214
+ ):
215
+ patch_ops["op"] = PatchOperation.REMOVE.value
216
+ del patch_ops["value"]
217
+ patch_ops_list.append(patch_ops)
218
+ patch.patch = patch_ops_list
204
219
  return patch
205
220
 
206
221
 
222
+ def _sort_array_entity_fields(
223
+ source: T,
224
+ destination: T,
225
+ array_entity_fields: Optional[List] = None,
226
+ ):
227
+ """
228
+ Sort the array entity fields to make sure the order is consistent
229
+ """
230
+ for field in array_entity_fields or []:
231
+ if hasattr(destination, field) and hasattr(source, field):
232
+ destination_attributes = getattr(destination, field)
233
+ source_attributes = getattr(source, field)
234
+
235
+ # Create a dictionary of destination attributes for easy lookup
236
+ destination_dict = {
237
+ model_str(attr.name): attr for attr in destination_attributes
238
+ }
239
+
240
+ updated_attributes = []
241
+ for source_attr in source_attributes or []:
242
+ # Update the destination attribute with the source attribute
243
+ destination_attr = destination_dict.get(model_str(source_attr.name))
244
+ if destination_attr:
245
+ updated_attributes.append(
246
+ source_attr.copy(update=destination_attr.__dict__)
247
+ )
248
+ # Remove the updated attribute from the destination dictionary
249
+ del destination_dict[model_str(source_attr.name)]
250
+ else:
251
+ updated_attributes.append(None)
252
+
253
+ # Combine the updated attributes with the remaining destination attributes
254
+ final_attributes = updated_attributes + list(destination_dict.values())
255
+ setattr(destination, field, final_attributes)
256
+
257
+
207
258
  def _determine_restricted_operation(
208
- patch_ops: Dict, restrict_update_fields: Optional[List] = None
259
+ patch_ops: Dict,
260
+ restrict_update_fields: Optional[List] = None,
209
261
  ) -> bool:
210
262
  """
211
263
  Only retain add operation for restrict_update_fields fields
@@ -21,6 +21,7 @@ from requests.exceptions import HTTPError
21
21
 
22
22
  from metadata.config.common import ConfigModel
23
23
  from metadata.ingestion.ometa.credentials import URL, get_api_version
24
+ from metadata.utils.execution_time_tracker import calculate_execution_time
24
25
  from metadata.utils.logger import ometa_logger
25
26
 
26
27
  logger = ometa_logger()
@@ -251,6 +252,7 @@ class REST:
251
252
 
252
253
  return None
253
254
 
255
+ @calculate_execution_time(context="GET")
254
256
  def get(self, path, data=None):
255
257
  """
256
258
  GET method
@@ -264,6 +266,7 @@ class REST:
264
266
  """
265
267
  return self._request("GET", path, data)
266
268
 
269
+ @calculate_execution_time(context="POST")
267
270
  def post(self, path, data=None):
268
271
  """
269
272
  POST method
@@ -277,6 +280,7 @@ class REST:
277
280
  """
278
281
  return self._request("POST", path, data)
279
282
 
283
+ @calculate_execution_time(context="PUT")
280
284
  def put(self, path, data=None):
281
285
  """
282
286
  PUT method
@@ -290,6 +294,7 @@ class REST:
290
294
  """
291
295
  return self._request("PUT", path, data)
292
296
 
297
+ @calculate_execution_time(context="PATCH")
293
298
  def patch(self, path, data=None):
294
299
  """
295
300
  PATCH method
@@ -308,6 +313,7 @@ class REST:
308
313
  headers={"Content-type": "application/json-patch+json"},
309
314
  )
310
315
 
316
+ @calculate_execution_time(context="DELETE")
311
317
  def delete(self, path, data=None):
312
318
  """
313
319
  DELETE method
@@ -15,7 +15,8 @@ To be used by OpenMetadata class
15
15
  """
16
16
  from typing import Dict
17
17
 
18
- from metadata.generated.schema.api.data.createCustomProperty import PropertyType
18
+ from metadata.generated.schema.type.customProperty import PropertyType
19
+ from metadata.generated.schema.type.entityReference import EntityReference
19
20
  from metadata.ingestion.models.custom_properties import (
20
21
  CustomPropertyDataTypes,
21
22
  CustomPropertyType,
@@ -54,16 +55,6 @@ class OMetaCustomPropertyMixin:
54
55
  f"/metadata/types/name/{entity_type}?category=field"
55
56
  )
56
57
 
57
- # Get the data type of the custom property
58
- if not ometa_custom_property.createCustomPropertyRequest.propertyType:
59
- custom_property_type = self.get_custom_property_type(
60
- data_type=ometa_custom_property.custom_property_type
61
- )
62
- property_type = PropertyType(id=custom_property_type.id, type="type")
63
- ometa_custom_property.createCustomPropertyRequest.propertyType = (
64
- property_type
65
- )
66
-
67
58
  resp = self.client.put(
68
59
  f"/metadata/types/{entity_schema.get('id')}",
69
60
  data=ometa_custom_property.createCustomPropertyRequest.json(),
@@ -78,3 +69,12 @@ class OMetaCustomPropertyMixin:
78
69
  """
79
70
  resp = self.client.get(f"/metadata/types/name/{data_type.value}?category=field")
80
71
  return CustomPropertyType(**resp)
72
+
73
+ def get_property_type_ref(self, data_type: CustomPropertyDataTypes) -> PropertyType:
74
+ """
75
+ Get the PropertyType for custom properties
76
+ """
77
+ custom_property_type = self.get_custom_property_type(data_type=data_type)
78
+ return PropertyType(
79
+ __root__=EntityReference(id=custom_property_type.id, type="type")
80
+ )
@@ -119,6 +119,7 @@ class OMetaPatchMixin(OMetaPatchMixinBase):
119
119
  destination: T,
120
120
  allowed_fields: Optional[Dict] = None,
121
121
  restrict_update_fields: Optional[List] = None,
122
+ array_entity_fields: Optional[List] = None,
122
123
  ) -> Optional[T]:
123
124
  """
124
125
  Given an Entity type and Source entity and Destination entity,
@@ -140,6 +141,7 @@ class OMetaPatchMixin(OMetaPatchMixinBase):
140
141
  destination=destination,
141
142
  allowed_fields=allowed_fields,
142
143
  restrict_update_fields=restrict_update_fields,
144
+ array_entity_fields=array_entity_fields,
143
145
  )
144
146
 
145
147
  if not patch:
@@ -418,7 +418,7 @@ class OpenMetadata(
418
418
  self,
419
419
  entity: Type[T],
420
420
  fields: Optional[List[str]] = None,
421
- limit: int = 1000,
421
+ limit: int = 100,
422
422
  params: Optional[Dict[str, str]] = None,
423
423
  skip_on_failure: bool = False,
424
424
  ) -> Iterable[T]:
@@ -62,6 +62,7 @@ from metadata.ingestion.models.ometa_classification import OMetaTagAndClassifica
62
62
  from metadata.ingestion.models.ometa_topic_data import OMetaTopicSampleData
63
63
  from metadata.ingestion.models.patch_request import (
64
64
  ALLOWED_COMMON_PATCH_FIELDS,
65
+ ARRAY_ENTITY_FIELDS,
65
66
  RESTRICT_UPDATE_LIST,
66
67
  PatchedEntity,
67
68
  PatchRequest,
@@ -82,7 +83,7 @@ from metadata.ingestion.ometa.ometa_api import OpenMetadata
82
83
  from metadata.ingestion.source.dashboard.dashboard_service import DashboardUsage
83
84
  from metadata.ingestion.source.database.database_service import DataModelLink
84
85
  from metadata.profiler.api.models import ProfilerResponse
85
- from metadata.utils.helpers import calculate_execution_time
86
+ from metadata.utils.execution_time_tracker import calculate_execution_time
86
87
  from metadata.utils.logger import get_log_name, ingestion_logger
87
88
 
88
89
  logger = ingestion_logger()
@@ -129,7 +130,7 @@ class MetadataRestSink(Sink): # pylint: disable=too-many-public-methods
129
130
  logger.debug(f"Processing Create request {type(record)}")
130
131
  return self.write_create_request(record)
131
132
 
132
- @calculate_execution_time
133
+ @calculate_execution_time(store=False)
133
134
  def _run(self, record: Entity, *_, **__) -> Either[Any]:
134
135
  """
135
136
  Default implementation for the single dispatch
@@ -179,6 +180,7 @@ class MetadataRestSink(Sink): # pylint: disable=too-many-public-methods
179
180
  destination=record.new_entity,
180
181
  allowed_fields=ALLOWED_COMMON_PATCH_FIELDS,
181
182
  restrict_update_fields=RESTRICT_UPDATE_LIST,
183
+ array_entity_fields=ARRAY_ENTITY_FIELDS,
182
184
  )
183
185
  patched_entity = PatchedEntity(new_entity=entity) if entity else None
184
186
  return Either(right=patched_entity)
@@ -661,7 +661,7 @@ class LookerSource(DashboardServiceSource):
661
661
  ],
662
662
  # Dashboards are created from the UI directly. They are not linked to a project
663
663
  # like LookML assets, but rather just organised in folders.
664
- project=self._get_dashboard_project(dashboard_details),
664
+ project=self.get_project_name(dashboard_details),
665
665
  sourceUrl=f"{clean_uri(self.service_connection.hostPort)}/dashboards/{dashboard_details.id}",
666
666
  service=self.context.dashboard_service,
667
667
  owner=self.get_owner_ref(dashboard_details=dashboard_details),
@@ -669,8 +669,7 @@ class LookerSource(DashboardServiceSource):
669
669
  yield Either(right=dashboard_request)
670
670
  self.register_record(dashboard_request=dashboard_request)
671
671
 
672
- @staticmethod
673
- def _get_dashboard_project(dashboard_details: LookerDashboard) -> Optional[str]:
672
+ def get_project_name(self, dashboard_details: LookerDashboard) -> Optional[str]:
674
673
  """
675
674
  Get dashboard project if the folder is informed
676
675
  """
@@ -680,7 +679,7 @@ class LookerSource(DashboardServiceSource):
680
679
  logger.debug(
681
680
  f"Cannot get folder name from dashboard [{dashboard_details.title}] - [{exc}]"
682
681
  )
683
- return None
682
+ return None
684
683
 
685
684
  @staticmethod
686
685
  def _clean_table_name(table_name: str) -> str:
@@ -124,6 +124,10 @@ class MetabaseClient:
124
124
  try:
125
125
  resp_dashboard = self.client.get(f"/dashboard/{dashboard_id}")
126
126
  if resp_dashboard:
127
+ # Small hack needed to support Metabase versions older than 0.48
128
+ # https://www.metabase.com/releases/metabase-48#fyi--breaking-changes
129
+ if "ordered_cards" in resp_dashboard:
130
+ resp_dashboard["dashcards"] = resp_dashboard["ordered_cards"]
127
131
  return MetabaseDashboardDetails(**resp_dashboard)
128
132
  except Exception:
129
133
  logger.debug(traceback.format_exc())
@@ -178,7 +178,7 @@ class MetabaseSource(DashboardServiceSource):
178
178
  Returns:
179
179
  Iterable[CreateChartRequest]
180
180
  """
181
- charts = dashboard_details.ordered_cards
181
+ charts = dashboard_details.dashcards
182
182
  for chart in charts:
183
183
  try:
184
184
  chart_details = chart.card
@@ -225,7 +225,7 @@ class MetabaseSource(DashboardServiceSource):
225
225
  if not db_service_name:
226
226
  return
227
227
  chart_list, dashboard_name = (
228
- dashboard_details.ordered_cards,
228
+ dashboard_details.dashcards,
229
229
  str(dashboard_details.id),
230
230
  )
231
231
  for chart in chart_list:
@@ -324,8 +324,9 @@ class MetabaseSource(DashboardServiceSource):
324
324
  self, chart_details: MetabaseChart, db_service_name: str, dashboard_name: str
325
325
  ) -> Iterable[Either[AddLineageRequest]]:
326
326
  table = self.client.get_table(chart_details.table_id)
327
+ table_name = table.name or table.display_name
327
328
 
328
- if table is None or table.display_name is None:
329
+ if table is None or table_name is None:
329
330
  return
330
331
 
331
332
  database_name = table.db.details.db if table.db and table.db.details else None
@@ -334,7 +335,7 @@ class MetabaseSource(DashboardServiceSource):
334
335
  database=database_name,
335
336
  service_name=db_service_name,
336
337
  database_schema=table.table_schema,
337
- table=table.display_name,
338
+ table=table_name,
338
339
  )
339
340
 
340
341
  to_fqn = fqn.build(
@@ -67,7 +67,7 @@ class MetabaseChart(BaseModel):
67
67
  display: Optional[str]
68
68
 
69
69
 
70
- class OrderedCard(BaseModel):
70
+ class DashCard(BaseModel):
71
71
  card: MetabaseChart
72
72
 
73
73
 
@@ -77,7 +77,7 @@ class MetabaseDashboardDetails(BaseModel):
77
77
  """
78
78
 
79
79
  description: Optional[str]
80
- ordered_cards: List[OrderedCard]
80
+ dashcards: List[DashCard]
81
81
  name: Optional[str]
82
82
  id: int
83
83
  collection_id: Optional[str]
@@ -152,6 +152,23 @@ class TableauSource(DashboardServiceSource):
152
152
  include_tags=self.source_config.includeTags,
153
153
  )
154
154
 
155
+ def _get_datamodel_sql_query(self, data_model: DataSource) -> Optional[str]:
156
+ """
157
+ Method to fetch the custom sql query from the tableau datamodels
158
+ """
159
+ try:
160
+ sql_queries = []
161
+ for table in data_model.upstreamTables or []:
162
+ for referenced_query in table.referencedByQueries or []:
163
+ sql_queries.append(referenced_query.query)
164
+ return "\n\n".join(sql_queries) or None
165
+ except Exception as exc:
166
+ logger.debug(traceback.format_exc())
167
+ logger.warning(
168
+ f"Error processing queries for datamodel [{data_model.id}]: {exc}"
169
+ )
170
+ return None
171
+
155
172
  def yield_datamodel(
156
173
  self, dashboard_details: TableauDashboard
157
174
  ) -> Iterable[Either[CreateDashboardDataModelRequest]]:
@@ -171,6 +188,7 @@ class TableauSource(DashboardServiceSource):
171
188
  dataModelType=DataModelType.TableauDataModel.value,
172
189
  serviceType=DashboardServiceType.Tableau.value,
173
190
  columns=self.get_column_info(data_model),
191
+ sql=self._get_datamodel_sql_query(data_model=data_model),
174
192
  )
175
193
  yield Either(right=data_model_request)
176
194
  self.register_record_datamodel(datamodel_request=data_model_request)
@@ -12,7 +12,7 @@
12
12
  """
13
13
  Source connection helper
14
14
  """
15
-
15
+ import traceback
16
16
  from typing import Any
17
17
 
18
18
  from pydantic import BaseModel
@@ -26,7 +26,14 @@ from metadata.generated.schema.security.credentials.gcpValues import (
26
26
  SingleProjectId,
27
27
  )
28
28
  from metadata.ingestion.source.connections import get_connection
29
+ from metadata.ingestion.source.database.bigquery.queries import (
30
+ BIGQUERY_FOREIGN_CONSTRAINTS,
31
+ BIGQUERY_TABLE_CONSTRAINTS,
32
+ )
29
33
  from metadata.utils.bigquery_utils import get_bigquery_client
34
+ from metadata.utils.logger import ingestion_logger
35
+
36
+ logger = ingestion_logger()
30
37
 
31
38
 
32
39
  class InspectorWrapper(BaseModel):
@@ -64,3 +71,63 @@ def get_inspector_details(
64
71
  inspector = inspect(engine)
65
72
 
66
73
  return InspectorWrapper(client=client, engine=engine, inspector=inspector)
74
+
75
+
76
+ def get_pk_constraint(
77
+ self, connection, table_name, schema=None, **kw
78
+ ): # pylint: disable=unused-argument
79
+ """
80
+ This function overrides to get primary key constraint
81
+ """
82
+ try:
83
+ table_constraints = connection.engine.execute(
84
+ BIGQUERY_TABLE_CONSTRAINTS.format(
85
+ project_id=connection.engine.url.host,
86
+ schema_name=schema,
87
+ table_name=table_name,
88
+ )
89
+ )
90
+ col_name = []
91
+ for table_constraint in table_constraints:
92
+ col_name.append(table_constraint.column_name)
93
+ return {"constrained_columns": tuple(col_name)}
94
+ except Exception as exc:
95
+ logger.debug(traceback.format_exc())
96
+ logger.warning(
97
+ f"Error while fetching primary key constraint error for table [{schema}.{table_name}]: {exc}"
98
+ )
99
+ return {"constrained_columns": []}
100
+
101
+
102
+ def get_foreign_keys(
103
+ self, connection, table_name, schema=None, **kw
104
+ ): # pylint: disable=unused-argument
105
+ """
106
+ This function overrides to get foreign key constraint
107
+ """
108
+ try:
109
+ table_constraints = connection.engine.execute(
110
+ BIGQUERY_FOREIGN_CONSTRAINTS.format(
111
+ project_id=connection.engine.url.host,
112
+ schema_name=schema,
113
+ table_name=table_name,
114
+ )
115
+ )
116
+ col_name = []
117
+ for table_constraint in table_constraints:
118
+ col_name.append(
119
+ {
120
+ "name": table_constraint.name,
121
+ "referred_schema": table_constraint.referred_schema,
122
+ "referred_table": table_constraint.referred_table,
123
+ "constrained_columns": [table_constraint.constrained_columns],
124
+ "referred_columns": [table_constraint.referred_columns],
125
+ }
126
+ )
127
+ return col_name
128
+ except Exception as exc:
129
+ logger.debug(traceback.format_exc())
130
+ logger.warning(
131
+ f"Error while fetching foreign key constraint error for table [{schema}.{table_name}]: {exc}"
132
+ )
133
+ return []
@@ -56,7 +56,11 @@ from metadata.ingestion.api.steps import InvalidSourceException
56
56
  from metadata.ingestion.models.ometa_classification import OMetaTagAndClassification
57
57
  from metadata.ingestion.ometa.ometa_api import OpenMetadata
58
58
  from metadata.ingestion.source.connections import get_test_connection_fn
59
- from metadata.ingestion.source.database.bigquery.helper import get_inspector_details
59
+ from metadata.ingestion.source.database.bigquery.helper import (
60
+ get_foreign_keys,
61
+ get_inspector_details,
62
+ get_pk_constraint,
63
+ )
60
64
  from metadata.ingestion.source.database.bigquery.models import (
61
65
  STORED_PROC_LANGUAGE_MAP,
62
66
  BigQueryStoredProcedure,
@@ -194,6 +198,8 @@ def _build_formatted_table_id(table):
194
198
  BigQueryDialect._build_formatted_table_id = ( # pylint: disable=protected-access
195
199
  _build_formatted_table_id
196
200
  )
201
+ BigQueryDialect.get_pk_constraint = get_pk_constraint
202
+ BigQueryDialect.get_foreign_keys = get_foreign_keys
197
203
 
198
204
 
199
205
  class BigquerySource(
@@ -245,7 +251,9 @@ class BigquerySource(
245
251
  test_connection_fn(
246
252
  self.metadata, inspector_details.engine, self.service_connection
247
253
  )
248
- if os.environ[GOOGLE_CREDENTIALS]:
254
+ # GOOGLE_CREDENTIALS may not have been set,
255
+ # to avoid key error, we use `get` for dict
256
+ if os.environ.get(GOOGLE_CREDENTIALS):
249
257
  self.temp_credentials_file_path.append(os.environ[GOOGLE_CREDENTIALS])
250
258
 
251
259
  def query_table_names_and_types(
@@ -436,7 +444,8 @@ class BigquerySource(
436
444
  inspector_details = get_inspector_details(
437
445
  database_name=database_name, service_connection=self.service_connection
438
446
  )
439
- self.temp_credentials_file_path.append(os.environ[GOOGLE_CREDENTIALS])
447
+ if os.environ.get(GOOGLE_CREDENTIALS):
448
+ self.temp_credentials_file_path.append(os.environ[GOOGLE_CREDENTIALS])
440
449
  self.client = inspector_details.client
441
450
  self.engine = inspector_details.engine
442
451
  self.inspector = inspector_details.inspector
@@ -58,6 +58,28 @@ BIGQUERY_TABLE_AND_TYPE = textwrap.dedent(
58
58
  """
59
59
  )
60
60
 
61
+ BIGQUERY_TABLE_CONSTRAINTS = textwrap.dedent(
62
+ """
63
+ SELECT *
64
+ FROM `{project_id}`.{schema_name}.INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE
65
+ WHERE table_name = '{table_name}' AND constraint_name LIKE '%pk$';
66
+ """
67
+ )
68
+
69
+ BIGQUERY_FOREIGN_CONSTRAINTS = textwrap.dedent(
70
+ """
71
+ SELECT
72
+ c.table_name AS referred_table,
73
+ r.table_schema as referred_schema,
74
+ r.constraint_name as name,
75
+ c.column_name as referred_columns,
76
+ c.column_name as constrained_columns
77
+ FROM `{project_id}`.{schema_name}.INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE c
78
+ JOIN `{project_id}`.{schema_name}.INFORMATION_SCHEMA.TABLE_CONSTRAINTS r ON c.constraint_name = r.constraint_name
79
+ WHERE r.constraint_type = 'FOREIGN KEY' AND r.table_name='{table_name}';
80
+ """
81
+ )
82
+
61
83
  BIGQUERY_GET_STORED_PROCEDURES = textwrap.dedent(
62
84
  """
63
85
  SELECT
@@ -0,0 +1,62 @@
1
+ # Copyright 2024 Collate
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software
7
+ # distributed under the License is distributed on an "AS IS" BASIS,
8
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9
+ # See the License for the specific language governing permissions and
10
+ # limitations under the License.
11
+ """A client for Google Cloud Bigtable that supports multiple projects."""
12
+ from functools import partial
13
+ from typing import List, Optional, Type
14
+
15
+ from google import auth
16
+ from google.cloud.bigtable import Client
17
+
18
+ NoProject = object()
19
+
20
+
21
+ class MultiProjectClient:
22
+ """Google Cloud Client does not support ad-hoc project switching. This class wraps the client and allows
23
+ switching between projects. If no project is specified, the client will not have a project set and will try
24
+ to resolve it from ADC.
25
+ Example usage:
26
+ ```
27
+ from google.cloud.bigtable import Client
28
+ client = MultiProjectClient(Client, project_ids=["project1", "project2"])
29
+ instances_project1 = client.list_instances("project1")
30
+ instances_project2 = client.list_instances("project2")
31
+ """
32
+
33
+ def __init__(
34
+ self,
35
+ client_class: Type[Client],
36
+ project_ids: Optional[List[str]] = None,
37
+ **client_kwargs,
38
+ ):
39
+ if project_ids:
40
+ self.clients = {
41
+ project_id: client_class(project=project_id, **client_kwargs)
42
+ for project_id in project_ids
43
+ }
44
+ else:
45
+ self.clients = {NoProject: client_class(**client_kwargs)}
46
+
47
+ def project_ids(self):
48
+ if NoProject in self.clients:
49
+ _, project_id = auth.default()
50
+ return [project_id]
51
+ return list(self.clients.keys())
52
+
53
+ def __getattr__(self, client_method):
54
+ """Return the underlying client method as a partial function so we can inject the project_id."""
55
+ return partial(self._call, client_method)
56
+
57
+ def _call(self, method, project_id, *args, **kwargs):
58
+ """Call the method on the client for the given project_id. The args and kwargs are passed through."""
59
+ client = self.clients.get(project_id, self.clients.get(NoProject))
60
+ if not client:
61
+ raise ValueError(f"Project {project_id} not found")
62
+ return getattr(client, method)(*args, **kwargs)