From f631994577ccf6918b698b5af7995ccbf9918d9f Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Mon, 11 Jul 2022 09:37:43 +0300 Subject: [PATCH 01/47] add powerbi report server ingestion source --- .../src/images/powerbireportserverlogo.png | Bin 0 -> 163473 bytes docs/cli.md | 1 + .../powerbi_report_server.md | 9 + .../powerbi_report_server_recipe.yml | 27 + .../examples/mce_files/data_platforms.json | 20 + metadata-ingestion/setup.py | 5 + .../source/powerbi_report_server/__init__.py | 0 .../source/powerbi_report_server/constants.py | 98 +++ .../powerbi_report_server/graphql_domain.py | 92 +++ .../powerbi_report_server/report_server.py | 726 ++++++++++++++++++ .../report_server_domain.py | 246 ++++++ .../golden_test_ingest.json | 382 +++++++++ .../test_powerbi_report_server.py | 119 +++ .../main/resources/boot/data_platforms.json | 12 +- 14 files changed, 1736 insertions(+), 1 deletion(-) create mode 100644 datahub-web-react/src/images/powerbireportserverlogo.png create mode 100644 metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server.md create mode 100644 metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server_recipe.yml create mode 100644 metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py create mode 100644 metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/constants.py create mode 100644 metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/graphql_domain.py create mode 100644 metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py create mode 100644 metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py create mode 100644 metadata-ingestion/tests/integration/powerbi_report_server/golden_test_ingest.json create mode 100644 metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py diff --git a/datahub-web-react/src/images/powerbireportserverlogo.png b/datahub-web-react/src/images/powerbireportserverlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..03519c2e3b0e3c46e1d9e379c2b0982cab99f6b2 GIT binary patch literal 163473 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%0ABY z<7fXjuFg%wYlxwuCOMnli8VX=Q6AkPmze`0w!M<7%8ZPO{m2UppL4N#x39{U{@i@_ zfejIB)P4+8p;ty72iq9)BRCc$O8Hpm{-r1| zvaU;v1TA^AT@T4gSdP^TL?9Zwvew{p3SK=RDMuc|>q&RVGLmOj4ysQ|_;?a9i3TEs zfdlVMXU#PtVΜ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 z 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
{G?cQ_foxMc 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 k_CI{B|9X&ykC|5?RtI-TJXTu7A>ebu None: + self.__config: PowerBiReportServerAPIConfig = config + self.__auth: HttpNtlmAuth = HttpNtlmAuth( + "{}\\{}".format(self.__config.workstation_name, self.__config.username), + self.__config.password, + ) + + def get_auth_credentials(self): + return self.__auth + + def get_users_policies(self) -> List[SystemPolicies]: + """ + Get User policy by Power Bi Report Server System + """ + user_list_endpoint: str = API_ENDPOINTS[Constant.SYSTEM_POLICIES] + # Replace place holders + user_list_endpoint = user_list_endpoint.format( + PBIRS_BASE_URL=self.__config.get_base_api_url + ) + # Hit PowerBi Report Server + LOGGER.info("Request to URL={}".format(user_list_endpoint)) + response = requests.get( + url=user_list_endpoint, auth=self.get_auth_credentials() + ) + + # Check if we got response from PowerBi Report Server + if response.status_code != 200: + LOGGER.warning( + "Failed to fetch User list from power-bi for, http_status={}, message={}".format( + response.status_code, response.text + ) + ) + raise ConnectionError("Failed to fetch the User list from the power-bi") + + users_dict: List[Any] = response.json()[Constant.VALUE] + # Iterate through response and create a list of PowerBiReportServerAPI.Dashboard + users: List[SystemPolicies] = [ + SystemPolicies.parse_obj(instance) for instance in users_dict + ] + return users + + def get_user_policies(self, user_name: str) -> Optional[SystemPolicies]: + users_policies = self.get_users_policies() + for user_policy in users_policies: + if user_policy.GroupUserName == user_name: + return user_policy + return None + + def get_report(self, report_id: str) -> Optional[Report]: + """ + Fetch the .rdl Report from PowerBiReportServer for the given Report Id + """ + if report_id is None: + LOGGER.info("Input value is None") + LOGGER.info("{}={}".format(Constant.ReportId, report_id)) + return None + + report_get_endpoint: str = API_ENDPOINTS[Constant.REPORT] + # Replace place holders + report_get_endpoint = report_get_endpoint.format( + PBIRS_BASE_URL=self.__config.get_base_api_url, + REPORT_ID=report_id, + ) + # Hit PowerBiReportServer + LOGGER.info("Request to Report URL={}".format(report_get_endpoint)) + response = requests.get( + url=report_get_endpoint, + auth=self.get_auth_credentials(), + ) + + # Check if we got response from PowerBi Report Server + if response.status_code != 200: + message: str = "Failed to fetch Report from power-bi-report-server for" + LOGGER.warning(message) + LOGGER.warning("{}={}".format(Constant.ReportId, report_id)) + raise ConnectionError(message) + + response_dict = response.json() + + return Report.parse_obj(response_dict) + + def get_powerbi_report(self, report_id: str) -> Optional[PowerBiReport]: + """ + Fetch the .pbix Report from PowerBiReportServer for the given Report Id + """ + if report_id is None: + LOGGER.info("Input value is None") + LOGGER.info("{}={}".format(Constant.ReportId, report_id)) + return None + + powerbi_report_get_endpoint: str = API_ENDPOINTS[Constant.POWERBI_REPORT] + # Replace place holders + powerbi_report_get_endpoint = powerbi_report_get_endpoint.format( + PBIRS_BASE_URL=self.__config.get_base_api_url, + POWERBI_REPORT_ID=report_id, + ) + # Hit PowerBiReportServer + LOGGER.info("Request to Report URL={}".format(powerbi_report_get_endpoint)) + response = requests.get( + url=powerbi_report_get_endpoint, + auth=self.get_auth_credentials(), + ) + + # Check if we got response from PowerBi Report Server + if response.status_code != 200: + message: str = "Failed to fetch Report from power-bi-report-server for" + LOGGER.warning(message) + LOGGER.warning("{}={}".format(Constant.ReportId, report_id)) + raise ConnectionError(message) + + response_dict = response.json() + return PowerBiReport.parse_obj(response_dict) + + def get_linked_report(self, report_id: str) -> Optional[LinkedReport]: + """ + Fetch the Mobile Report from PowerBiReportServer for the given Report Id + """ + if report_id is None: + LOGGER.info("Input value is None") + LOGGER.info("{}={}".format(Constant.ReportId, report_id)) + return None + + linked_report_get_endpoint: str = API_ENDPOINTS[Constant.LINKED_REPORT] + # Replace place holders + linked_report_get_endpoint = linked_report_get_endpoint.format( + PBIRS_BASE_URL=self.__config.get_base_api_url, + LINKED_REPORT_ID=report_id, + ) + # Hit PowerBiReportServer + LOGGER.info("Request to Report URL={}".format(linked_report_get_endpoint)) + response = requests.get( + url=linked_report_get_endpoint, + auth=self.get_auth_credentials(), + ) + + # Check if we got response from PowerBi Report Server + if response.status_code != 200: + message: str = "Failed to fetch Report from power-bi-report-server for" + LOGGER.warning(message) + LOGGER.warning("{}={}".format(Constant.ReportId, report_id)) + raise ConnectionError(message) + + response_dict = response.json() + + return LinkedReport.parse_obj(response_dict) + + def get_mobile_report(self, report_id: str) -> Optional[MobileReport]: + """ + Fetch the Mobile Report from PowerBiReportServer for the given Report Id + """ + if report_id is None: + LOGGER.info("Input value is None") + LOGGER.info("{}={}".format(Constant.ReportId, report_id)) + return None + + mobile_report_get_endpoint: str = API_ENDPOINTS[Constant.MOBILE_REPORT] + # Replace place holders + mobile_report_get_endpoint = mobile_report_get_endpoint.format( + PBIRS_BASE_URL=self.__config.get_base_api_url, + MOBILE_REPORT_ID=report_id, + ) + # Hit PowerBi ReportServer + LOGGER.info("Request to Report URL={}".format(mobile_report_get_endpoint)) + response = requests.get( + url=mobile_report_get_endpoint, + auth=self.get_auth_credentials(), + ) + + # Check if we got response from PowerBi Report Server + if response.status_code != 200: + message: str = "Failed to fetch Report from power-bi-report-server for" + LOGGER.warning(message) + LOGGER.warning("{}={}".format(Constant.ReportId, report_id)) + raise ConnectionError(message) + + response_dict = response.json() + + return MobileReport.parse_obj(response_dict) + + def get_all_reports(self) -> List[Any]: + """ + Fetch all Reports from PowerBiReportServer + """ + report_types_mapping: Dict[str, Any] = { + Constant.REPORTS: Report, + Constant.MOBILE_REPORTS: MobileReport, + Constant.LINKED_REPORTS: LinkedReport, + Constant.POWERBI_REPORTS: PowerBiReport, + } + + reports: List[Any] = [] + for report_type in report_types_mapping.keys(): + + report_get_endpoint: str = API_ENDPOINTS[report_type] + # Replace place holders + report_get_endpoint = report_get_endpoint.format( + PBIRS_BASE_URL=self.__config.get_base_api_url, + ) + # Hit PowerBi ReportServer + LOGGER.info("Request to Report URL={}".format(report_get_endpoint)) + response = requests.get( + url=report_get_endpoint, + auth=self.get_auth_credentials(), + ) + + # Check if we got response from PowerBi Report Server + if response.status_code != 200: + message: str = "Failed to fetch Report from power-bi-report-server for" + LOGGER.warning(message) + LOGGER.warning("{}={}".format(Constant.ReportId, report_type)) + raise ValueError(message) + + response_dict = response.json()["value"] + if response_dict: + reports.extend( + report_types_mapping[report_type].parse_obj(report) + for report in response_dict + ) + + return reports + +class UserDao: + def __init__(self, config: PowerBiReportServerDashboardSourceConfig): + self.__config = config + + def _run_query(self, query) -> Dict[str, Any]: + request = requests.post(url=self.__config.graphql_url, json={"query": query}) + if request.status_code == 200: + return request.json() + else: + raise Exception( + "Query failed to run by returning code of {}. {}".format( + request.status_code, query + ) + ) + + def _get_owner_by_name(self, user_name: str) -> Dict[str, Any]: + get_owner = f"""{{ + search(input: {{ type: CORP_USER, query: "{user_name}"}}){{ + searchResults{{ + entity{{ + urn + type + ...on CorpUser {{ + username + urn + type + properties {{ + active + displayName + email + title + }} + }} + }} + matchedFields {{ + name + value + }} + }} + }} + }} + """ + + result = self._run_query(get_owner) + return result + + @staticmethod + def _filter_values(response: Dict[str, Any], mask: str) -> Optional[Dict[str, Any]]: + users = response["data"]["search"]["searchResults"] + for user in users: + if user["matchedFields"][0]["value"] == mask: + return user["entity"] + return None + + def get_owner_by_name(self, user_name: str) -> Optional[CorpUser]: + response = self._get_owner_by_name(user_name=user_name) + filtered_response = self._filter_values(response=response, mask=user_name) + if filtered_response: + return CorpUser.parse_obj(filtered_response) + + user_data = dict( + urn=f"urn:li:corpuser:{user_name}", + type=Constant.CORP_USER, + username=user_name, + properties=dict(active=True, displayName=user_name, email=""), + ) + return CorpUser.parse_obj(user_data) + + +class Mapper: + """ + Transfrom PowerBi Report Server concept Report to DataHub concept Dashboard + """ + + class EquableMetadataWorkUnit(MetadataWorkUnit): + """ + We can add EquableMetadataWorkUnit to set. + This will avoid passing same MetadataWorkUnit to DataHub Ingestion framework. + """ + + def __eq__(self, instance): + return self.id == self.id + + def __hash__(self): + return id(self.id) + + def __init__(self, config: PowerBiReportServerDashboardSourceConfig): + self.__config = config + + @staticmethod + def new_mcp( + entity_type, + entity_urn, + aspect_name, + aspect, + change_type=ChangeTypeClass.UPSERT, + ): + """ + Create MCP + """ + return MetadataChangeProposalWrapper( + entityType=entity_type, + changeType=change_type, + entityUrn=entity_urn, + aspectName=aspect_name, + aspect=aspect, + ) + + def __to_work_unit( + self, mcp: MetadataChangeProposalWrapper + ) -> EquableMetadataWorkUnit: + return Mapper.EquableMetadataWorkUnit( + id="{PLATFORM}-{ENTITY_URN}-{ASPECT_NAME}".format( + PLATFORM=self.__config.platform_name, + ENTITY_URN=mcp.entityUrn, + ASPECT_NAME=mcp.aspectName, + ), + mcp=mcp, + ) + + @staticmethod + def to_urn_set(mcps: List[MetadataChangeProposalWrapper]) -> List[str]: + return list( + OrderedSet( + [ + mcp.entityUrn + for mcp in mcps + if mcp is not None and mcp.entityUrn is not None + ] + ) + ) + + def __to_datahub_dashboard( + self, + report: Report, + chart_mcps: List[MetadataChangeProposalWrapper], + user_mcps: List[MetadataChangeProposalWrapper], + ) -> List[MetadataChangeProposalWrapper]: + """ + Map PowerBi Report Server report to Datahub Dashboard + """ + + dashboard_urn = builder.make_dashboard_urn( + self.__config.platform_name, report.get_urn_part() + ) + + chart_urn_list: List[str] = self.to_urn_set(chart_mcps) + user_urn_list: List[str] = self.to_urn_set(user_mcps) + + def chart_custom_properties( + _report: Report, + ) -> dict: + return { + "chartCount": str(0), + "workspaceName": "PowerBI Report Server", + "workspaceId": self.__config.host_port, + "dataSource": str( + [report.ConnectionString for report in _report.DataSources] + ) + if _report.DataSources + else "", + } + + # DashboardInfo mcp + dashboard_info_cls = DashboardInfoClass( + description=report.Description or "", + title=report.Name or "", + charts=chart_urn_list, + lastModified=ChangeAuditStamps(), + dashboardUrl=report.get_web_url(self.__config.get_base_url), + customProperties={**chart_custom_properties(report)}, + ) + + info_mcp = self.new_mcp( + entity_type=Constant.DASHBOARD, + entity_urn=dashboard_urn, + aspect_name=Constant.DASHBOARD_INFO, + aspect=dashboard_info_cls, + ) + + # removed status mcp + removed_status_mcp = self.new_mcp( + entity_type=Constant.DASHBOARD, + entity_urn=dashboard_urn, + aspect_name=Constant.STATUS, + aspect=StatusClass(removed=False), + ) + + # dashboardKey mcp + dashboard_key_cls = DashboardKeyClass( + dashboardTool=self.__config.platform_name, + dashboardId=Constant.DASHBOARD_ID.format(report.Id), + ) + + # Dashboard key + dashboard_key_mcp = self.new_mcp( + entity_type=Constant.DASHBOARD, + entity_urn=dashboard_urn, + aspect_name=Constant.DASHBOARD_KEY, + aspect=dashboard_key_cls, + ) + + # Dashboard Ownership + owners = [ + OwnerClass(owner=user_urn, type=OwnershipTypeClass.BUSINESS_OWNER) + for user_urn in user_urn_list + if user_urn is not None + ] + ownership = OwnershipClass(owners=owners) + # Dashboard owner MCP + owner_mcp = self.new_mcp( + entity_type=Constant.DASHBOARD, + entity_urn=dashboard_urn, + aspect_name=Constant.OWNERSHIP, + aspect=ownership, + ) + + # Dashboard browsePaths + browse_path = BrowsePathsClass( + paths=[report.get_browse_path("powerbi_report_server", self.__config.host)] + ) + browse_path_mcp = self.new_mcp( + entity_type=Constant.DASHBOARD, + entity_urn=dashboard_urn, + aspect_name=Constant.BROWSERPATH, + aspect=browse_path, + ) + + return [ + browse_path_mcp, + info_mcp, + removed_status_mcp, + dashboard_key_mcp, + owner_mcp, + ] + + def to_datahub_user(self, user: CorpUser) -> List[MetadataChangeProposalWrapper]: + """ + Map PowerBi ReportServer user to datahub user + """ + LOGGER.info("Converting user {} to datahub's user".format(user.username)) + + # Create an URN for User + user_urn = builder.make_user_urn(user.get_urn_part()) + + user_info_instance = CorpUserInfoClass( + displayName=user.properties.displayName, + email=user.properties.email, + title=user.properties.title, + active=True, + ) + + info_mcp = self.new_mcp( + entity_type=Constant.CORP_USER, + entity_urn=user_urn, + aspect_name=Constant.CORP_USER_INFO, + aspect=user_info_instance, + ) + + # removed status mcp + status_mcp = self.new_mcp( + entity_type=Constant.CORP_USER, + entity_urn=user_urn, + aspect_name=Constant.STATUS, + aspect=StatusClass(removed=False), + ) + + user_key = CorpUserKeyClass(username=user.username) + + user_key_mcp = self.new_mcp( + entity_type=Constant.CORP_USER, + entity_urn=user_urn, + aspect_name=Constant.CORP_USER_KEY, + aspect=user_key, + ) + + return [info_mcp, status_mcp, user_key_mcp] + + def to_datahub_work_units(self, report: Report) -> Set[EquableMetadataWorkUnit]: + mcps = [] + + LOGGER.info("Converting Dashboard={} to DataHub Dashboard".format(report.Name)) + # Convert user to CorpUser + user_mcps = self.to_datahub_user(report.UserInfo) + # Convert tiles to charts + ds_mcps: List[Any] + chart_mcps: List[Any] + # ds_mcps = self.__to_datahub_dataset(report) + chart_mcps, ds_mcps = [], [] # self.to_datahub_chart(dashboard.tiles) + # Lets convert Dashboard to DataHub Dashboard + dashboard_mcps = self.__to_datahub_dashboard(report, chart_mcps, user_mcps) + + # Now add MCPs in sequence + mcps.extend(ds_mcps) + mcps.extend(user_mcps) + mcps.extend(chart_mcps) + mcps.extend(dashboard_mcps) + + # Convert MCP to work_units + work_units = map(self.__to_work_unit, mcps) + # Return set of work_unit + return OrderedSet([wu for wu in work_units if wu is not None]) + + +@dataclass +class PowerBiReportServerDashboardSourceReport(SourceReport): + scanned_report: int = 0 + filtered_reports: List[str] = dataclass_field(default_factory=list) + + def report_scanned(self, count: int = 1) -> None: + self.scanned_report += count + + def report_dropped(self, view: str) -> None: + self.filtered_reports.append(view) + + +@platform_name("PowerBIReportServer") +@config_class(PowerBiReportServerDashboardSourceConfig) +@support_status(SupportStatus.UNKNOWN) +@capability(SourceCapability.OWNERSHIP, "Enabled by default") +class PowerBiReportServerDashboardSource(Source): + """ + This plugin extracts the following: + + - Power BI Dashboards, tiles, datasets + - Names, descriptions and URLs of Dashboard and tile + - Owners of Dashboards + + ## Configuration Notes + + See the + 1. [Microsoft AD App Creation doc] + (https://docs.microsoft.com/en-us/power-bi/developer/embedded/embed-service-principal) + for the steps to create a app client ID and secret. + 2. Login to Power BI as Admin and from `Tenant settings` allow below permissions. + - Allow service principles to use Power BI APIs + - Allow service principals to use read-only Power BI admin APIs + - Enhance admin APIs responses with detailed metadata + """ + + source_config: PowerBiReportServerDashboardSourceConfig + report: PowerBiReportServerDashboardSourceReport + accessed_dashboards: int = 0 + + def __init__( + self, config: PowerBiReportServerDashboardSourceConfig, ctx: PipelineContext + ): + super().__init__(ctx) + self.source_config = config + self.report = PowerBiReportServerDashboardSourceReport() + self.auth = PowerBiReportServerAPI(self.source_config).get_auth_credentials() + self.powerbi_client = PowerBiReportServerAPI(self.source_config) + self.mapper = Mapper(config) + self.user_dao = UserDao(config) + + @classmethod + def create(cls, config_dict, ctx): + config = PowerBiReportServerDashboardSourceConfig.parse_obj(config_dict) + return cls(config, ctx) + + def get_workunits(self) -> Iterable[MetadataWorkUnit]: + """ + Datahub Ingestion framework invoke this method + """ + LOGGER.info("PowerBiReportServer plugin execution is started") + + reports = self.powerbi_client.get_all_reports() + + for report in reports: + try: + report.UserInfo = self.user_dao.get_owner_by_name( + user_name=report.DisplayName + ) + except ValidationError as e: + message = "Error ({}) occurred while loading User {}(id={})".format( + e, report.Name, report.Id + ) + LOGGER.exception(message, e) + self.report.report_warning(report.Id, message) + finally: + # Increase Dashboard and tiles count in report + self.report.report_scanned(count=1) + # Convert PowerBi Report Server Dashboard and child entities + # to Datahub work unit to ingest into Datahub + workunits = self.mapper.to_datahub_work_units(report) + for workunit in workunits: + # Add workunit to report + self.report.report_workunit(workunit) + # Return workunit to Datahub Ingestion framework + yield workunit + + def get_report(self) -> SourceReport: + return self.report + + def close(self): + pass diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py new file mode 100644 index 0000000000000..1ddda1dc2a2fe --- /dev/null +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py @@ -0,0 +1,246 @@ +from datetime import datetime +from typing import Any, Dict, List, Optional + +from pydantic import BaseModel, validator + + +class CatalogItem(BaseModel): + Id: str + Name: str + Description: Optional[str] + Path: str + Type: Any + Hidden: bool + Size: int + ModifiedBy: Optional[str] + ModifiedDate: Optional[datetime] + CreatedBy: Optional[str] + CreatedDate: Optional[datetime] + ParentFolderId: Optional[str] + ContentType: Optional[str] + Content: str + IsFavorite: bool + UserInfo: Any + DisplayName: Optional[str] + HasDataSources: bool = False + DataSources: Optional[List["DataSource"]] = [] + + @validator("DisplayName", always=True) + def validate_diplay_name(cls, value, values): # noqa: N805 + if values["CreatedBy"]: + return values["CreatedBy"].split("\\")[-1] + return "" + + def get_urn_part(self): + return "reports.{}".format(self.Id) + + def get_web_url(self, base_reports_url: str): + return "{}powerbi{}".format(base_reports_url, self.Path) + + def get_browse_path(self, base_folder: str, workspace: str): + return "/{}/{}{}".format(base_folder, workspace, self.Path) + + +class DataSet(CatalogItem): + HasParameters: bool + QueryExecutionTimeOut: int + + def get_urn_part(self): + return "datasets.{}".format(self.Id) + + def __members(self): + return (self.Id,) + + def __eq__(self, instance): + return ( + isinstance(instance, DataSet) and self.__members() == instance.__members() + ) + + def __hash__(self): + return hash(self.__members()) + + +class DataModelDataSource(BaseModel): + AuthType: Optional[str] + SupportedAuthTypes: List[Optional[str]] + Kind: str + ModelConnectionName: str + Secret: str + Type: Optional[str] + Username: str + + +class CredentialsByUser(BaseModel): + DisplayText: str + UseAsWindowsCredentials: bool + + +class CredentialsInServer(BaseModel): + UserName: str + Password: str + UseAsWindowsCredentials: bool + ImpersonateAuthenticatedUser: bool + + +class ParameterValue(BaseModel): + Name: str + Value: str + IsValueFieldReference: str + + +class ExtensionSettings(BaseModel): + Extension: str + ParameterValues: ParameterValue + + +class Subscription(BaseModel): + Id: str + Owner: str + IsDataDriven: bool + Description: str + Report: str + IsActive: bool + EventType: str + ScheduleDescription: str + LastRunTime: datetime + LastStatus: str + ExtensionSettings: ExtensionSettings + DeliveryExtension: str + LocalizedDeliveryExtensionName: str + ModifiedBy: str + ModifiedDate: datetime + ParameterValues: ParameterValue + + +class MetaData(BaseModel): + is_relational: bool + + +class DataSource(CatalogItem): + Name: str = "" + Path: str = "" + IsEnabled: bool + ConnectionString: str + DataModelDataSource: Optional[DataModelDataSource] + DataSourceSubType: Optional[str] + DataSourceType: Optional[str] + IsOriginalConnectionStringExpressionBased: bool + IsConnectionStringOverridden: bool + CredentialsByUser: Optional[CredentialsByUser] + CredentialsInServer: Optional[CredentialsInServer] + IsReference: bool + Subscriptions: Optional[Subscription] + MetaData: Optional[MetaData] + + def __members(self): + return (self.Id,) + + def __eq__(self, instance): + return ( + isinstance(instance, DataSource) + and self.__members() == instance.__members() + ) + + def __hash__(self): + return hash(self.__members()) + + +class Comment(BaseModel): + Id: str + ItemId: str + UserName: str + ThreadId: str + AttachmentPath: str + Text: str + CreatedDate: datetime + ModifiedDate: datetime + + +class ExcelWorkbook(CatalogItem): + Comments: Comment + + +class Role(BaseModel): + Name: str + Description: str + + +class SystemPolicies(BaseModel): + GroupUserName: str + Roles: List[Role] + + +class Report(CatalogItem): + HasDataSources: bool + HasSharedDataSets: bool + HasParameters: bool + + +class PowerBiReport(CatalogItem): + HasDataSources: bool + + +class Extension(BaseModel): + ExtensionType: str + Name: str + LocalizedName: str + Visible: bool + + +class Folder(CatalogItem): + """Folder""" + + +class DrillThroughTarget(BaseModel): + DrillThroughTargetType: str + + +class Value(BaseModel): + Value: str + Goal: int + Status: int + TrendSet: List[int] + + +class Kpi(CatalogItem): + ValuerFormat: str + Visualization: str + DrillThroughTarget: DrillThroughTarget + Currency: str + Values: Value + Data: Dict[str, str] + + +class LinkedReport(CatalogItem): + HasParemeters: bool + Link: str + + +class Manifest(BaseModel): + Resorces: List[Dict[str, List]] + + +class MobileReport(CatalogItem): + AllowCaching: bool + Manifest: Manifest + + +class PowerBIReport(CatalogItem): + HasDataSources: bool + + +class Resources(CatalogItem): + """Resources""" + + +class System(BaseModel): + ReportServerAbsoluteUrl: str + ReportServerRelativeUrl: str + WebPortalRelativeUrl: str + ProductName: str + ProductVersion: str + ProductType: str + TimeZone: str + + +CatalogItem.update_forward_refs() diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/golden_test_ingest.json b/metadata-ingestion/tests/integration/powerbi_report_server/golden_test_ingest.json new file mode 100644 index 0000000000000..f7f8f285c1c4c --- /dev/null +++ b/metadata-ingestion/tests/integration/powerbi_report_server/golden_test_ingest.json @@ -0,0 +1,382 @@ +[ + { + "auditHeader": null, + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_book,DEV)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "datasetProperties", + "aspect": { + "value": "{\"customProperties\": {}, \"description\": \"issue_book\", \"tags\": []}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_book,DEV)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.member,DEV)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "datasetProperties", + "aspect": { + "value": "{\"customProperties\": {}, \"description\": \"member\", \"tags\": []}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.member,DEV)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "datasetProperties", + "aspect": { + "value": "{\"customProperties\": {}, \"description\": \"issue_history\", \"tags\": []}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User1@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserInfo", + "aspect": { + "value": "{\"active\": true, \"displayName\": \"User1\", \"email\": \"User1@foo.com\", \"title\": \"User1\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User1@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User1@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserKey", + "aspect": { + "value": "{\"username\": \"User1@foo.com\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User2@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserInfo", + "aspect": { + "value": "{\"active\": true, \"displayName\": \"User2\", \"email\": \"User2@foo.com\", \"title\": \"User2\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User2@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User2@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserKey", + "aspect": { + "value": "{\"username\": \"User2@foo.com\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "chart", + "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "chartInfo", + "aspect": { + "value": "{\"customProperties\": {\"datasetId\": \"05169CD2-E713-41E6-9600-1D8066D95445\", \"reportId\": \"\", \"datasetWebUrl\": \"http://localhost/groups/64ED5CAD-7C10-4684-8180-826122881108/datasets/05169CD2-E713-41E6-9600-1D8066D95445\", \"createdFrom\": \"Dataset\"}, \"title\": \"test_tiles\", \"description\": \"test_tiles\", \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"inputs\": [{\"string\": \"urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_book,DEV)\"}, {\"string\": \"urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.member,DEV)\"}, {\"string\": \"urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)\"}]}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "chart", + "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "chart", + "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "chartKey", + "aspect": { + "value": "{\"dashboardTool\": \"powerbi\", \"chartId\": \"powerbi.linkedin.com/charts/B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "browsePaths", + "aspect": { + "value": "{\"paths\": [\"/powerbi/64ED5CAD-7C10-4684-8180-826122881108\"]}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "dashboardInfo", + "aspect": { + "value": "{\"customProperties\": {\"chartCount\": \"1\", \"workspaceName\": \"foo\", \"workspaceId\": \"7D668CAD-7FFC-4505-9215-655BCA5BEBAE\"}, \"title\": \"test_dashboard\", \"description\": \"test_dashboard\", \"charts\": [\"urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)\"], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"https://localhost/dashboards/web/1\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "dashboardKey", + "aspect": { + "value": "{\"dashboardTool\": \"powerbi\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/7D668CAD-7FFC-4505-9215-655BCA5BEBAE\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "ownership", + "aspect": { + "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:users.User1@foo.com\", \"type\": \"CONSUMER\"}, {\"owner\": \"urn:li:corpuser:users.User2@foo.com\", \"type\": \"CONSUMER\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + } + ] \ No newline at end of file diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py new file mode 100644 index 0000000000000..ef0a017a2fb5c --- /dev/null +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py @@ -0,0 +1,119 @@ +from datetime import datetime +from unittest import mock +from unittest.mock import patch + +from freezegun import freeze_time + +from datahub.ingestion.run.pipeline import Pipeline + +from datahub.ingestion.source.powerbi_report_server.report_server_domain import ( + PowerBiReport, Report, +) +from tests.test_helpers import mce_helpers + +FROZEN_TIME = "2022-07-08 07:00:00" + +POWERBI_REPORT_SERVER_API = "datahub.ingestion.source.powerbi_report_server.report_server.PowerBiReportServerAPI" +POWERBI_REPORT_SERVER_USER_DAO_GET_OWNER = "datahub.ingestion.source.powerbi_report_server.report_server.UserDao.get_owner_by_name" + + +@freeze_time(FROZEN_TIME) +@patch(POWERBI_REPORT_SERVER_USER_DAO_GET_OWNER) +def test_powerbi_report_server_ingest(pytestconfig, tmp_path, mock_time): + mocked_client = mock.MagicMock() + # Set the datasource mapping dictionary + with mock.patch(POWERBI_REPORT_SERVER_API) as mock_sdk: + mock_sdk.return_value = mocked_client + # Mock the PowerBi Dashboard API response + mocked_client.get_all_reports.return_value = [ + PowerBiReport( + Id="c2259cbb-472f-4ee4-897a-97cf24d943db", + Name="6B536811-2E00-4961-9845-8559BA16C122", + Description=None, + Path="/path/to/6B536811-2E00-4961-9845-8559BA16C122", + Type="PowerBIReport", + Hidden=False, + Size=1010101, + ModifiedBy="TEST_USER", + ModifiedDate=datetime.now(), + CreatedBy="TEST_USER", + CreatedDate=datetime.now(), + ParentFolderId="47495172-89ab-455f-a446-fffd3cf239cb", + ContentType=None, + Content="", + IsFavorite=False, + UserInfo=None, + DisplayName="TEST_USER", + HasDataSources=True, + DataSources=[], + ), + Report( + Id="ee56dc21-248a-4138-a446-ee5ab1fc938b", + Name="Test_rdl", + Description=None, + Path="/path/to/Test_rdl", + Type="Report", + Hidden=False, + Size=1010101, + ModifiedBy="TEST_USER", + ModifiedDate=datetime.now(), + CreatedBy="TEST_USER", + CreatedDate=datetime.now(), + ParentFolderId="47495172-89ab-455f-a446-fffd3cf239cb", + ContentType=None, + Content="", + IsFavorite=False, + UserInfo=None, + DisplayName="TEST_USER", + HasDataSources=True, + DataSources=[], + HasSharedDataSets=False, + HasParameters=False, + ) + + ] + + + test_resources_dir = ( + pytestconfig.rootpath / "tests/integration/powerbi_report_server" + ) + + pipeline = Pipeline.create( + { + "run_id": "powerbi-report-server-test", + "source": { + "type": "powerbi-report-server", + "config": { + "username": "foo", + "password": "bar", + "workstation_name": "0B0C960B-FCDF-4D0F-8C45-2E03BB59DDEB", + "host_port": "64ED5CAD-7C10-4684-8180-826122881108", + "server_alias": "64ED5CAD", + "graphql_url": "http://localhost:8080/api/graphql", + "report_virtual_directory_name": "Reports", + "report_server_virtual_directory_name": "ReportServer", + "dataset_type_mapping": { + "PostgreSql": "postgres", + "Oracle": "oracle", + }, + "env": "DEV", + }, + }, + "sink": { + "type": "file", + "config": { + "filename": f"{tmp_path}/powerbi_report_server_mces.json", + }, + }, + } + ) + + pipeline.run() + pipeline.raise_from_status() + mce_out_file = "golden_test_ingest.json" + + mce_helpers.check_golden_file( + pytestconfig, + output_path=tmp_path / "powerbi_report_server_mces.json", + golden_path=f"{test_resources_dir}/{mce_out_file}", + ) diff --git a/metadata-service/war/src/main/resources/boot/data_platforms.json b/metadata-service/war/src/main/resources/boot/data_platforms.json index 2825f05e69407..227b008b59313 100644 --- a/metadata-service/war/src/main/resources/boot/data_platforms.json +++ b/metadata-service/war/src/main/resources/boot/data_platforms.json @@ -514,5 +514,15 @@ "type": "OTHERS", "logoUrl": "/assets/platforms/deltalakelogo.png" } - } + }, + { + "urn": "urn:li:dataPlatform:powerbi-report-server", + "aspect": { + "datasetNameDelimiter": ".", + "name": "powerbi-report-server", + "displayName": "PowerBI Report Server", + "type": "OTHERS", + "logoUrl": "/assets/platforms/powerbireportserverlogo.png" + } + }, ] From a4db4f2327c7dbd6fcc3c3434a16a9a4cdd5f73e Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Mon, 11 Jul 2022 12:31:49 +0300 Subject: [PATCH 02/47] change domain fields to lower case --- .../source/powerbi_report_server/constants.py | 2 +- .../powerbi_report_server/graphql_domain.py | 37 ++- .../powerbi_report_server/report_server.py | 46 ++-- .../report_server_domain.py | 260 +++++++++--------- 4 files changed, 178 insertions(+), 167 deletions(-) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/constants.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/constants.py index 54e3e6eb0b7ab..92560a11b90eb 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/constants.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/constants.py @@ -5,7 +5,7 @@ class CreatedFrom(Enum): REPORT = "Report" DATASET = "Dataset" VISUALIZATION = "Visualization" - UNKNOWN = "UNKNOWN" + UNKNOWN = "Unknown" class RelationshipDirection(Enum): diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/graphql_domain.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/graphql_domain.py index 1d982dd13677e..a43b6c9446c16 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/graphql_domain.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/graphql_domain.py @@ -1,18 +1,18 @@ from datetime import datetime from typing import List, Optional -from pydantic import BaseModel +from pydantic import BaseModel, Field -from powerbireportserver.constants import RelationshipDirection +from .constants import RelationshipDirection class CorpUserEditableInfo(BaseModel): - displayName: str + display_name: str = Field(alias="displayName") title: str - aboutMe: Optional[str] + about_me: Optional[str] = Field(alias="aboutMe") teams: Optional[List[str]] skills: Optional[List[str]] - pictureLink: Optional[str] + picture_link: Optional[str] = Field(alias="pictureLink") class CorpUserEditableProperties(CorpUserEditableInfo): @@ -45,20 +45,16 @@ class EntityRelationshipsResult(BaseModel): class CorpUserProperties(BaseModel): active: bool - displayName: str + display_name: str = Field(alias="displayName") email: str title: Optional[str] manager: Optional["CorpUser"] - departmentId: Optional[int] - departmentName: Optional[str] - firstName: Optional[str] - lastName: Optional[str] - fullName: Optional[str] - countryCode: Optional[str] - - -class CorpUserInfo(CorpUserProperties): - """Corp User Info""" + department_id: Optional[int] = Field(alias="departmentId") + department_name: Optional[str] = Field(alias="departmentName") + first_name: Optional[str] = Field(alias="firstName") + last_name: Optional[str] = Field(alias="lastName") + full_name: Optional[str] = Field(alias="fullName") + country_code: Optional[str] = Field(alias="countryCode") class CorpUser(BaseModel): @@ -66,13 +62,14 @@ class CorpUser(BaseModel): type: str username: str properties: CorpUserProperties - editableProperties: Optional[CorpUserEditableProperties] + editable_properties: Optional[CorpUserEditableProperties] = Field( + alias="editableProperties" + ) status: Optional[CorpUserStatus] tags: Optional[GlobalTags] relationships: Optional[EntityRelationshipsResult] - info: Optional[CorpUserInfo] - editableInfo: Optional[CorpUserEditableInfo] - globalTags: Optional[GlobalTags] + editableInfo: Optional[CorpUserEditableInfo] = Field(alias="editableInfo") + global_tags: Optional[GlobalTags] = Field(alias="globalTags") def get_urn_part(self): return "{}".format(self.username) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py index 8e74ba5cf90c8..638d2517c7c41 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py @@ -4,11 +4,17 @@ # ######################################################### import logging -from dataclasses import dataclass, field as dataclass_field -from typing import Any, Dict, Iterable, List, Optional, Set, Union +from dataclasses import dataclass +from dataclasses import field as dataclass_field +from typing import Any, Dict, Iterable, List, Optional, Set -import datahub.emitter.mce_builder as builder import requests +from orderedset import OrderedSet +from pydantic import ValidationError +from pydantic.fields import Field +from requests_ntlm import HttpNtlmAuth + +import datahub.emitter.mce_builder as builder from datahub.configuration.common import AllowDenyPattern from datahub.configuration.source_common import EnvBasedSourceConfigBase from datahub.emitter.mcp import MetadataChangeProposalWrapper @@ -37,12 +43,7 @@ OwnershipTypeClass, StatusClass, ) -from orderedset import OrderedSet -from pydantic import ValidationError -from pydantic.fields import Field -from requests_ntlm import HttpNtlmAuth -# Logger instance from .constants import API_ENDPOINTS, Constant from .graphql_domain import CorpUser from .report_server_domain import ( @@ -98,7 +99,7 @@ def host(self): class PowerBiReportServerDashboardSourceConfig(PowerBiReportServerAPIConfig): - platform_name: str = "powerbi_report_server" + platform_name: str = "powerbi" platform_urn: str = builder.make_data_platform_urn(platform=platform_name) report_pattern: AllowDenyPattern = AllowDenyPattern.allow_all() chart_pattern: AllowDenyPattern = AllowDenyPattern.allow_all() @@ -328,6 +329,7 @@ def get_all_reports(self) -> List[Any]: return reports + class UserDao: def __init__(self, config: PowerBiReportServerDashboardSourceConfig): self.__config = config @@ -485,16 +487,16 @@ def chart_custom_properties( "workspaceName": "PowerBI Report Server", "workspaceId": self.__config.host_port, "dataSource": str( - [report.ConnectionString for report in _report.DataSources] + [report.ConnectionString for report in _report.data_sources] ) - if _report.DataSources + if _report.data_sources else "", } # DashboardInfo mcp dashboard_info_cls = DashboardInfoClass( - description=report.Description or "", - title=report.Name or "", + description=report.description or "", + title=report.name or "", charts=chart_urn_list, lastModified=ChangeAuditStamps(), dashboardUrl=report.get_web_url(self.__config.get_base_url), @@ -519,7 +521,7 @@ def chart_custom_properties( # dashboardKey mcp dashboard_key_cls = DashboardKeyClass( dashboardTool=self.__config.platform_name, - dashboardId=Constant.DASHBOARD_ID.format(report.Id), + dashboardId=Constant.DASHBOARD_ID.format(report.id), ) # Dashboard key @@ -574,7 +576,7 @@ def to_datahub_user(self, user: CorpUser) -> List[MetadataChangeProposalWrapper] user_urn = builder.make_user_urn(user.get_urn_part()) user_info_instance = CorpUserInfoClass( - displayName=user.properties.displayName, + displayName=user.properties.display_name, email=user.properties.email, title=user.properties.title, active=True, @@ -609,9 +611,9 @@ def to_datahub_user(self, user: CorpUser) -> List[MetadataChangeProposalWrapper] def to_datahub_work_units(self, report: Report) -> Set[EquableMetadataWorkUnit]: mcps = [] - LOGGER.info("Converting Dashboard={} to DataHub Dashboard".format(report.Name)) + LOGGER.info("Converting Dashboard={} to DataHub Dashboard".format(report.name)) # Convert user to CorpUser - user_mcps = self.to_datahub_user(report.UserInfo) + user_mcps = self.to_datahub_user(report.user_info) # Convert tiles to charts ds_mcps: List[Any] chart_mcps: List[Any] @@ -694,19 +696,21 @@ def get_workunits(self) -> Iterable[MetadataWorkUnit]: """ LOGGER.info("PowerBiReportServer plugin execution is started") + # Fetch PowerBiReportServer reports for given url + # workspace = self.powerbi_client.get_workspace(self.source_config.workspace_id) reports = self.powerbi_client.get_all_reports() for report in reports: try: - report.UserInfo = self.user_dao.get_owner_by_name( - user_name=report.DisplayName + report.user_info = self.user_dao.get_owner_by_name( + user_name=report.display_name ) except ValidationError as e: message = "Error ({}) occurred while loading User {}(id={})".format( - e, report.Name, report.Id + e, report.name, report.id ) LOGGER.exception(message, e) - self.report.report_warning(report.Id, message) + self.report.report_warning(report.id, message) finally: # Increase Dashboard and tiles count in report self.report.report_scanned(count=1) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py index 1ddda1dc2a2fe..3416e66d42abd 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py @@ -1,55 +1,57 @@ from datetime import datetime from typing import Any, Dict, List, Optional -from pydantic import BaseModel, validator +from pydantic import BaseModel, Field, validator class CatalogItem(BaseModel): - Id: str - Name: str - Description: Optional[str] - Path: str - Type: Any - Hidden: bool - Size: int - ModifiedBy: Optional[str] - ModifiedDate: Optional[datetime] - CreatedBy: Optional[str] - CreatedDate: Optional[datetime] - ParentFolderId: Optional[str] - ContentType: Optional[str] - Content: str - IsFavorite: bool - UserInfo: Any - DisplayName: Optional[str] - HasDataSources: bool = False - DataSources: Optional[List["DataSource"]] = [] - - @validator("DisplayName", always=True) + id: str = Field(alias="Id") + name: str = Field(alias="Name") + description: Optional[str] = Field(alias="Description") + path: str = Field(alias="Path") + type: Any = Field(alias="Type") + hidden: bool = Field(alias="Hidden") + size: int = Field(alias="Size") + modified_by: Optional[str] = Field(alias="ModifiedBy") + modified_date: Optional[datetime] = Field(alias="ModifiedDate") + created_by: Optional[str] = Field(alias="CreatedBy") + created_date: Optional[datetime] = Field(alias="CreatedDate") + parent_folder_id: Optional[str] = Field(alias="ParentFolderId") + content_type: Optional[str] = Field(alias="ContentType") + content: str = Field(alias="Content") + is_favorite: bool = Field(alias="IsFavorite") + user_info: Any = Field(alias="UserInfo") + display_name: Optional[str] = Field(alias="DisplayName") + has_data_sources: bool = Field(default=False, alias="HasDataSources") + data_sources: Optional[List["DataSource"]] = Field( + default_factory=list, alias="DataSources" + ) + + @validator("display_name", always=True) def validate_diplay_name(cls, value, values): # noqa: N805 - if values["CreatedBy"]: - return values["CreatedBy"].split("\\")[-1] + if values["created_by"]: + return values["created_by"].split("\\")[-1] return "" def get_urn_part(self): - return "reports.{}".format(self.Id) + return "reports.{}".format(self.id) def get_web_url(self, base_reports_url: str): - return "{}powerbi{}".format(base_reports_url, self.Path) + return "{}powerbi{}".format(base_reports_url, self.path) def get_browse_path(self, base_folder: str, workspace: str): - return "/{}/{}{}".format(base_folder, workspace, self.Path) + return "/{}/{}{}".format(base_folder, workspace, self.path) class DataSet(CatalogItem): - HasParameters: bool - QueryExecutionTimeOut: int + has_parameters: bool = Field(alias="HasParameters") + query_execution_time_out: int = Field(alias="QueryExecutionTimeOut") def get_urn_part(self): - return "datasets.{}".format(self.Id) + return "datasets.{}".format(self.id) def __members(self): - return (self.Id,) + return (self.id,) def __eq__(self, instance): return ( @@ -61,55 +63,57 @@ def __hash__(self): class DataModelDataSource(BaseModel): - AuthType: Optional[str] - SupportedAuthTypes: List[Optional[str]] - Kind: str - ModelConnectionName: str - Secret: str - Type: Optional[str] - Username: str + auth_type: Optional[str] = Field(alias="AuthType") + supported_auth_types: List[Optional[str]] = Field(alias="SupportedAuthTypes") + kind: str = Field(alias="Kind") + model_connection_name: str = Field(alias="ModelConnectionName") + secret: str = Field(alias="Secret") + type: Optional[str] = Field(alias="Type") + username: str = Field(alias="Username") class CredentialsByUser(BaseModel): - DisplayText: str - UseAsWindowsCredentials: bool + display_text: str = Field(alias="DisplayText") + use_as_windows_credentials: bool = Field(alias="UseAsWindowsCredentials") class CredentialsInServer(BaseModel): - UserName: str - Password: str - UseAsWindowsCredentials: bool - ImpersonateAuthenticatedUser: bool + username: str = Field(alias="UserName") + password: str = Field(alias="Password") + use_as_windows_credentials: bool = Field(alias="UseAsWindowsCredentials") + impersonate_authenticated_user: bool = Field(alias="ImpersonateAuthenticatedUser") class ParameterValue(BaseModel): - Name: str - Value: str - IsValueFieldReference: str + name: str = Field(alias="Name") + value: str = Field(alias="Value") + is_value_field_reference: str = Field(alias="IsValueFieldReference") class ExtensionSettings(BaseModel): - Extension: str - ParameterValues: ParameterValue + extension: str = Field(alias="Extension") + parameter_values: ParameterValue = Field(alias="ParameterValues") class Subscription(BaseModel): - Id: str - Owner: str - IsDataDriven: bool - Description: str - Report: str - IsActive: bool - EventType: str - ScheduleDescription: str - LastRunTime: datetime - LastStatus: str - ExtensionSettings: ExtensionSettings - DeliveryExtension: str - LocalizedDeliveryExtensionName: str - ModifiedBy: str - ModifiedDate: datetime - ParameterValues: ParameterValue + id: str = Field(alias="Id") + owner: str = Field(alias="Owner") + is_data_driven: bool = Field(alias="IsDataDriven") + description: str = Field(alias="Description") + report: str = Field(alias="Report") + is_active: bool = Field(alias="IsActive") + event_type: str = Field(alias="EventType") + schedule_description: str = Field(alias="ScheduleDescription") + last_run_time: datetime = Field(alias="LastRunTime") + last_status: str = Field(alias="LastStatus") + extension_settings: ExtensionSettings = Field(alias="ExtensionSettings") + delivery_extension: str = Field(alias="DeliveryExtension") + localized_delivery_extension_name: str = Field( + alias="LocalizedDeliveryExtensionName" + ) + modified_by: str = Field(alias="ModifiedBy") + modified_date: datetime = Field(alias="ModifiedDate") + parameter_values: ParameterValue = Field(alias="ParameterValues") class MetaData(BaseModel): @@ -117,23 +121,29 @@ class MetaData(BaseModel): class DataSource(CatalogItem): - Name: str = "" - Path: str = "" - IsEnabled: bool - ConnectionString: str - DataModelDataSource: Optional[DataModelDataSource] - DataSourceSubType: Optional[str] - DataSourceType: Optional[str] - IsOriginalConnectionStringExpressionBased: bool - IsConnectionStringOverridden: bool - CredentialsByUser: Optional[CredentialsByUser] - CredentialsInServer: Optional[CredentialsInServer] - IsReference: bool - Subscriptions: Optional[Subscription] - MetaData: Optional[MetaData] + name: str = Field(default="", alias="Name") + path: str = Field(default="", alias="Path") + is_enabled: bool = Field(alias="IsEnabled") + connection_string: str = Field(alias="ConnectionString") + data_model_data_source: Optional[DataModelDataSource] = Field( + alias="DataModelDataSource" + ) + data_source_sub_type: Optional[str] = Field(alias="DataSourceSubType") + data_source_type: Optional[str] = Field(alias="DataSourceType") + is_original_connection_string_expression_based: bool = Field( + alias="IsOriginalConnectionStringExpressionBased" + ) + is_connection_string_overridden: bool = Field(alias="IsConnectionStringOverridden") + credentials_by_user: Optional[CredentialsByUser] = Field(alias="CredentialsByUser") + credentials_in_server: Optional[CredentialsInServer] = Field( + alias="CredentialsInServer" + ) + is_reference: bool = Field(alias="IsReference") + subscriptions: Optional[Subscription] = Field(alias="Subscriptions") + meta_data: Optional[MetaData] = Field(alias="MetaData") def __members(self): - return (self.Id,) + return (self.id,) def __eq__(self, instance): return ( @@ -146,45 +156,45 @@ def __hash__(self): class Comment(BaseModel): - Id: str - ItemId: str - UserName: str - ThreadId: str - AttachmentPath: str - Text: str - CreatedDate: datetime - ModifiedDate: datetime + id: str = Field(alias="Id") + item_id: str = Field(alias="ItemId") + username: str = Field(alias="UserName") + thread_id: str = Field(alias="ThreadId") + attachment_path: str = Field(alias="AttachmentPath") + text: str = Field(alias="Text") + created_date: datetime = Field(alias="CreatedDate") + modified_date: datetime = Field(alias="ModifiedDate") class ExcelWorkbook(CatalogItem): - Comments: Comment + comments: Comment = Field(alias="Comments") class Role(BaseModel): - Name: str - Description: str + name: str = Field(alias="Name") + description: str = Field(alias="Description") class SystemPolicies(BaseModel): - GroupUserName: str - Roles: List[Role] + group_user_name: str = Field(alias="GroupUserName") + roles: List[Role] = Field(alias="Roles") class Report(CatalogItem): - HasDataSources: bool - HasSharedDataSets: bool - HasParameters: bool + has_data_sources: bool = Field(alias="HasDataSources") + has_shared_data_sets: bool = Field(alias="HasSharedDataSets") + has_parameters: bool = Field(alias="HasParameters") class PowerBiReport(CatalogItem): - HasDataSources: bool + has_data_sources: bool = Field(alias="HasDataSources") class Extension(BaseModel): - ExtensionType: str - Name: str - LocalizedName: str - Visible: bool + extension_type: str = Field(alias="ExtensionType") + name: str = Field(alias="Name") + localized_name: str = Field(alias="LocalizedName") + Visible: bool = Field(alias="Visible") class Folder(CatalogItem): @@ -192,41 +202,41 @@ class Folder(CatalogItem): class DrillThroughTarget(BaseModel): - DrillThroughTargetType: str + drill_through_target_type: str = Field(alias="DrillThroughTargetType") class Value(BaseModel): - Value: str - Goal: int - Status: int - TrendSet: List[int] + value: str = Field(alias="Value") + goal: int = Field(alias="Goal") + status: int = Field(alias="Status") + trend_set: List[int] = Field(alias="TrendSet") class Kpi(CatalogItem): - ValuerFormat: str - Visualization: str - DrillThroughTarget: DrillThroughTarget - Currency: str - Values: Value - Data: Dict[str, str] + value_format: str = Field(alias="ValueFormat") + visualization: str = Field(alias="Visualization") + drill_through_target: DrillThroughTarget = Field(alias="DrillThroughTarget") + currency: str = Field(alias="Currency") + values: Value = Field(alias="Values") + data: Dict[str, str] = Field(alias="Data") class LinkedReport(CatalogItem): - HasParemeters: bool - Link: str + has_parameters: bool = Field(alias="HasParameters") + link: str = Field(alias="Link") class Manifest(BaseModel): - Resorces: List[Dict[str, List]] + resources: List[Dict[str, List]] = Field(alias="Resources") class MobileReport(CatalogItem): - AllowCaching: bool - Manifest: Manifest + allow_caching: bool = Field(alias="AllowCaching") + manifest: Manifest = Field(alias="Manifest") class PowerBIReport(CatalogItem): - HasDataSources: bool + has_data_sources: bool = Field(alias="HasDataSources") class Resources(CatalogItem): @@ -234,13 +244,13 @@ class Resources(CatalogItem): class System(BaseModel): - ReportServerAbsoluteUrl: str - ReportServerRelativeUrl: str - WebPortalRelativeUrl: str - ProductName: str - ProductVersion: str - ProductType: str - TimeZone: str + report_server_absolute_url: str = Field(alias="ReportServerAbsoluteUrl") + report_server_relative_url: str = Field(alias="ReportServerRelativeUrl") + web_portal_relative_url: str = Field(alias="WebPortalRelativeUrl") + product_name: str = Field(alias="ProductName") + product_version: str = Field(alias="ProductVersion") + product_type: str = Field(alias="ProductType") + time_zone: str = Field(alias="TimeZone") CatalogItem.update_forward_refs() From f67e25ec1f4e4a0cc21d2ad669e7a374c48862df Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Mon, 11 Jul 2022 14:18:52 +0300 Subject: [PATCH 03/47] change imports --- metadata-ingestion/setup.py | 2 +- .../datahub/ingestion/source/powerbi_report_server/__init__.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/metadata-ingestion/setup.py b/metadata-ingestion/setup.py index ef08342e8f1eb..1084a3fdd0cfc 100644 --- a/metadata-ingestion/setup.py +++ b/metadata-ingestion/setup.py @@ -511,7 +511,7 @@ def get_long_description(): "starburst-trino-usage = datahub.ingestion.source.usage.starburst_trino_usage:TrinoUsageSource", "nifi = datahub.ingestion.source.nifi:NifiSource", "powerbi = datahub.ingestion.source.powerbi:PowerBiDashboardSource", - "powerbi-report-server = datahub.ingestion.source.powerbi_report_server.report_server:PowerBiReportServerDashboardSource", + "powerbi-report-server = datahub.ingestion.source.powerbi_report_server:PowerBiReportServerDashboardSource", "iceberg = datahub.ingestion.source.iceberg.iceberg:IcebergSource", "vertica = datahub.ingestion.source.sql.vertica:VerticaSource", "presto-on-hive = datahub.ingestion.source.sql.presto_on_hive:PrestoOnHiveSource", diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py index e69de29bb2d1d..5130d2e3334df 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py @@ -0,0 +1 @@ +from datahub.ingestion.source.powerbi_report_server.report_server import PowerBiReportServerDashboardSource \ No newline at end of file From 7eb76a364e9fbbb8cf907c1ad63e7efa86699a55 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Mon, 11 Jul 2022 15:06:52 +0300 Subject: [PATCH 04/47] fix naming, fix function name, remove unused properties --- .../source/powerbi_report_server/report_server.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py index 638d2517c7c41..195c85660d055 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py @@ -37,7 +37,6 @@ CorpUserKeyClass, DashboardInfoClass, DashboardKeyClass, - DatasetPropertiesClass, OwnerClass, OwnershipClass, OwnershipTypeClass, @@ -152,7 +151,7 @@ def get_users_policies(self) -> List[SystemPolicies]: def get_user_policies(self, user_name: str) -> Optional[SystemPolicies]: users_policies = self.get_users_policies() for user_policy in users_policies: - if user_policy.GroupUserName == user_name: + if user_policy.group_user_name == user_name: return user_policy return None @@ -479,15 +478,14 @@ def __to_datahub_dashboard( chart_urn_list: List[str] = self.to_urn_set(chart_mcps) user_urn_list: List[str] = self.to_urn_set(user_mcps) - def chart_custom_properties( + def custom_properties( _report: Report, ) -> dict: return { - "chartCount": str(0), "workspaceName": "PowerBI Report Server", "workspaceId": self.__config.host_port, "dataSource": str( - [report.ConnectionString for report in _report.data_sources] + [report.connection_string for report in _report.data_sources] ) if _report.data_sources else "", @@ -500,7 +498,7 @@ def chart_custom_properties( charts=chart_urn_list, lastModified=ChangeAuditStamps(), dashboardUrl=report.get_web_url(self.__config.get_base_url), - customProperties={**chart_custom_properties(report)}, + customProperties={**custom_properties(report)}, ) info_mcp = self.new_mcp( From bab4da0004085baf4da9e836e117438999115c1e Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Mon, 11 Jul 2022 15:13:54 +0300 Subject: [PATCH 05/47] fix yml --- .../powerbi-report-server/powerbi_report_server_recipe.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server_recipe.yml b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server_recipe.yml index 84b6bdb6b689a..c40075d2c7749 100644 --- a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server_recipe.yml +++ b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server_recipe.yml @@ -24,4 +24,4 @@ source: PostgreSql: postgres Oracle: oracle sink: - # sink configs \ No newline at end of file + # sink configs From 2e49c375fb831ab282f663bf55411cedf70ddcd3 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Mon, 11 Jul 2022 15:19:56 +0300 Subject: [PATCH 06/47] fix data platforms --- .../war/src/main/resources/boot/data_platforms.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metadata-service/war/src/main/resources/boot/data_platforms.json b/metadata-service/war/src/main/resources/boot/data_platforms.json index 227b008b59313..919f8c71d59e8 100644 --- a/metadata-service/war/src/main/resources/boot/data_platforms.json +++ b/metadata-service/war/src/main/resources/boot/data_platforms.json @@ -515,7 +515,7 @@ "logoUrl": "/assets/platforms/deltalakelogo.png" } }, - { + { "urn": "urn:li:dataPlatform:powerbi-report-server", "aspect": { "datasetNameDelimiter": ".", @@ -524,5 +524,5 @@ "type": "OTHERS", "logoUrl": "/assets/platforms/powerbireportserverlogo.png" } - }, + } ] From 95746ae2e7d53d46d94168aacfbe7936cc879b9c Mon Sep 17 00:00:00 2001 From: RChygir <30907647+RChygir@users.noreply.github.com> Date: Mon, 11 Jul 2022 20:22:30 +0300 Subject: [PATCH 07/47] fix: fix black check --- .../ingestion/source/powerbi_report_server/__init__.py | 4 +++- .../powerbi_report_server/test_powerbi_report_server.py | 7 +++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py index 5130d2e3334df..8e1b838c67c63 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py @@ -1 +1,3 @@ -from datahub.ingestion.source.powerbi_report_server.report_server import PowerBiReportServerDashboardSource \ No newline at end of file +from datahub.ingestion.source.powerbi_report_server.report_server import ( + PowerBiReportServerDashboardSource, +) diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py index ef0a017a2fb5c..a990159645d42 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py @@ -7,7 +7,8 @@ from datahub.ingestion.run.pipeline import Pipeline from datahub.ingestion.source.powerbi_report_server.report_server_domain import ( - PowerBiReport, Report, + PowerBiReport, + Report, ) from tests.test_helpers import mce_helpers @@ -69,11 +70,9 @@ def test_powerbi_report_server_ingest(pytestconfig, tmp_path, mock_time): DataSources=[], HasSharedDataSets=False, HasParameters=False, - ) - + ), ] - test_resources_dir = ( pytestconfig.rootpath / "tests/integration/powerbi_report_server" ) From 3cb0e743109c26e0f68599a9fa5c5ca37aadb812 Mon Sep 17 00:00:00 2001 From: RChygir <30907647+RChygir@users.noreply.github.com> Date: Tue, 12 Jul 2022 10:39:12 +0300 Subject: [PATCH 08/47] docs: add note to source docs --- .../sources/powerbi-report-server/powerbi_report_server.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server.md b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server.md index ba3cea5559e2f..c0e8977e49dae 100644 --- a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server.md +++ b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server.md @@ -7,3 +7,7 @@ | `Mobile Report` | `Dashboard` | | `Linked Report` | `Dashboard` | | `Dataset, Datasource` | `N/A` | + +### Note + +Current [PowerBI](https://powerbi.microsoft.com/en-us/what-is-power-bi/) module used only for work with [PowerBI Report Server](https://powerbi.microsoft.com/en-us/report-server/). From 7a1f7d5ec09ac143665104673b842771a7c3df5e Mon Sep 17 00:00:00 2001 From: RChygir <30907647+RChygir@users.noreply.github.com> Date: Tue, 12 Jul 2022 10:48:06 +0300 Subject: [PATCH 09/47] docs: add title --- .../docs/sources/powerbi-report-server/powerbi_report_server.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server.md b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server.md index c0e8977e49dae..f7afccb5169eb 100644 --- a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server.md +++ b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server.md @@ -1,3 +1,5 @@ +# PowerBI Report Server + ## Concept mapping | Power BI Report Server | Datahub | From fc0b7e4a3b4b60dda98fef0736de0364235f8c95 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Tue, 12 Jul 2022 15:58:07 +0300 Subject: [PATCH 10/47] fix: run linters --- .../powerbi_report_server/test_powerbi_report_server.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py index ef0a017a2fb5c..b50ac1aaaa57a 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py @@ -5,9 +5,9 @@ from freezegun import freeze_time from datahub.ingestion.run.pipeline import Pipeline - from datahub.ingestion.source.powerbi_report_server.report_server_domain import ( - PowerBiReport, Report, + PowerBiReport, + Report, ) from tests.test_helpers import mce_helpers @@ -69,11 +69,9 @@ def test_powerbi_report_server_ingest(pytestconfig, tmp_path, mock_time): DataSources=[], HasSharedDataSets=False, HasParameters=False, - ) - + ), ] - test_resources_dir = ( pytestconfig.rootpath / "tests/integration/powerbi_report_server" ) From 1b3e609152bae6c59e11ca188c689edb3ac15b6f Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Fri, 22 Jul 2022 10:37:05 +0300 Subject: [PATCH 11/47] fix: fix tests, report module --- .../powerbi_report_server_recipe.yml | 7 +- .../source/powerbi_report_server/__init__.py | 5 + .../powerbi_report_server/report_server.py | 240 ++------ .../golden_test_ingest.json | 532 +++++------------- .../test_powerbi_report_server.py | 57 +- 5 files changed, 221 insertions(+), 620 deletions(-) diff --git a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server_recipe.yml b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server_recipe.yml index c40075d2c7749..9c144e1687f6f 100644 --- a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server_recipe.yml +++ b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server_recipe.yml @@ -1,5 +1,5 @@ source: - type: powerbireportserver.report_server.PowerBiReportServerDashboardSource + type: powerbi-report-server config: # Your Power BI Report Server Windows username username: username @@ -19,9 +19,6 @@ source: report_virtual_directory_name: Reports # Your Power BI Report Server base virtual directory name for report server report_server_virtual_directory_name: ReportServer - # dataset_type_mapping is fixed mapping of Power BI datasources type to equivalent Datahub "data platform" dataset - dataset_type_mapping: - PostgreSql: postgres - Oracle: oracle + sink: # sink configs diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py index 8e1b838c67c63..d61be97c5f6ca 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py @@ -1,3 +1,8 @@ from datahub.ingestion.source.powerbi_report_server.report_server import ( PowerBiReportServerDashboardSource, ) +from datahub.ingestion.source.powerbi_report_server.report_server_domain import ( + CorpUser, + PowerBiReport, + Report, +) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py index 195c85660d055..6c76656e6d491 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py @@ -12,6 +12,7 @@ from orderedset import OrderedSet from pydantic import ValidationError from pydantic.fields import Field +from requests.exceptions import ConnectionError from requests_ntlm import HttpNtlmAuth import datahub.emitter.mce_builder as builder @@ -45,13 +46,7 @@ from .constants import API_ENDPOINTS, Constant from .graphql_domain import CorpUser -from .report_server_domain import ( - LinkedReport, - MobileReport, - PowerBiReport, - Report, - SystemPolicies, -) +from .report_server_domain import LinkedReport, MobileReport, PowerBiReport, Report LOGGER = logging.getLogger(__name__) @@ -71,18 +66,20 @@ class PowerBiReportServerAPIConfig(EnvBasedSourceConfigBase): report_server_virtual_directory_name: str = Field( description="Report Server Virtual Directory URL name" ) - dataset_type_mapping: Dict[str, str] = Field( - default={}, - description="Mapping of Power BI DataSource type to Datahub DataSet.", - ) scan_timeout: int = Field( default=60, description="time in seconds to wait for Power BI metadata scan result.", ) @property - def get_base_api_url(self): - return "http://{}/{}/api/v2.0/".format( + def get_base_api_http_url(self): + return "http://{}/{}/api/v2.0".format( + self.host_port, self.report_virtual_directory_name + ) + + @property + def get_base_api_https_url(self): + return "https://{}/{}/api/v2.0".format( self.host_port, self.report_virtual_directory_name ) @@ -98,7 +95,7 @@ def host(self): class PowerBiReportServerDashboardSourceConfig(PowerBiReportServerAPIConfig): - platform_name: str = "powerbi" + platform_name: str = "powerbi-report-server" platform_urn: str = builder.make_data_platform_urn(platform=platform_name) report_pattern: AllowDenyPattern = AllowDenyPattern.allow_all() chart_pattern: AllowDenyPattern = AllowDenyPattern.allow_all() @@ -114,177 +111,32 @@ def __init__(self, config: PowerBiReportServerAPIConfig) -> None: self.__config.password, ) + @property def get_auth_credentials(self): return self.__auth - def get_users_policies(self) -> List[SystemPolicies]: - """ - Get User policy by Power Bi Report Server System - """ - user_list_endpoint: str = API_ENDPOINTS[Constant.SYSTEM_POLICIES] - # Replace place holders - user_list_endpoint = user_list_endpoint.format( - PBIRS_BASE_URL=self.__config.get_base_api_url - ) - # Hit PowerBi Report Server - LOGGER.info("Request to URL={}".format(user_list_endpoint)) - response = requests.get( - url=user_list_endpoint, auth=self.get_auth_credentials() - ) - - # Check if we got response from PowerBi Report Server - if response.status_code != 200: - LOGGER.warning( - "Failed to fetch User list from power-bi for, http_status={}, message={}".format( - response.status_code, response.text - ) + def requests_get(self, url_http: str, url_https: str, content_type: str): + try: + LOGGER.info("Request to Report URL={}".format(url_https)) + response = requests.get( + url=url_https, + auth=self.get_auth_credentials, + verify=False, + ) + except ConnectionError: + LOGGER.info("Request to Report URL={}".format(url_http)) + response = requests.get( + url=url_http, + auth=self.get_auth_credentials, ) - raise ConnectionError("Failed to fetch the User list from the power-bi") - - users_dict: List[Any] = response.json()[Constant.VALUE] - # Iterate through response and create a list of PowerBiReportServerAPI.Dashboard - users: List[SystemPolicies] = [ - SystemPolicies.parse_obj(instance) for instance in users_dict - ] - return users - - def get_user_policies(self, user_name: str) -> Optional[SystemPolicies]: - users_policies = self.get_users_policies() - for user_policy in users_policies: - if user_policy.group_user_name == user_name: - return user_policy - return None - - def get_report(self, report_id: str) -> Optional[Report]: - """ - Fetch the .rdl Report from PowerBiReportServer for the given Report Id - """ - if report_id is None: - LOGGER.info("Input value is None") - LOGGER.info("{}={}".format(Constant.ReportId, report_id)) - return None - - report_get_endpoint: str = API_ENDPOINTS[Constant.REPORT] - # Replace place holders - report_get_endpoint = report_get_endpoint.format( - PBIRS_BASE_URL=self.__config.get_base_api_url, - REPORT_ID=report_id, - ) - # Hit PowerBiReportServer - LOGGER.info("Request to Report URL={}".format(report_get_endpoint)) - response = requests.get( - url=report_get_endpoint, - auth=self.get_auth_credentials(), - ) - - # Check if we got response from PowerBi Report Server - if response.status_code != 200: - message: str = "Failed to fetch Report from power-bi-report-server for" - LOGGER.warning(message) - LOGGER.warning("{}={}".format(Constant.ReportId, report_id)) - raise ConnectionError(message) - - response_dict = response.json() - - return Report.parse_obj(response_dict) - - def get_powerbi_report(self, report_id: str) -> Optional[PowerBiReport]: - """ - Fetch the .pbix Report from PowerBiReportServer for the given Report Id - """ - if report_id is None: - LOGGER.info("Input value is None") - LOGGER.info("{}={}".format(Constant.ReportId, report_id)) - return None - - powerbi_report_get_endpoint: str = API_ENDPOINTS[Constant.POWERBI_REPORT] - # Replace place holders - powerbi_report_get_endpoint = powerbi_report_get_endpoint.format( - PBIRS_BASE_URL=self.__config.get_base_api_url, - POWERBI_REPORT_ID=report_id, - ) - # Hit PowerBiReportServer - LOGGER.info("Request to Report URL={}".format(powerbi_report_get_endpoint)) - response = requests.get( - url=powerbi_report_get_endpoint, - auth=self.get_auth_credentials(), - ) - - # Check if we got response from PowerBi Report Server - if response.status_code != 200: - message: str = "Failed to fetch Report from power-bi-report-server for" - LOGGER.warning(message) - LOGGER.warning("{}={}".format(Constant.ReportId, report_id)) - raise ConnectionError(message) - - response_dict = response.json() - return PowerBiReport.parse_obj(response_dict) - - def get_linked_report(self, report_id: str) -> Optional[LinkedReport]: - """ - Fetch the Mobile Report from PowerBiReportServer for the given Report Id - """ - if report_id is None: - LOGGER.info("Input value is None") - LOGGER.info("{}={}".format(Constant.ReportId, report_id)) - return None - - linked_report_get_endpoint: str = API_ENDPOINTS[Constant.LINKED_REPORT] - # Replace place holders - linked_report_get_endpoint = linked_report_get_endpoint.format( - PBIRS_BASE_URL=self.__config.get_base_api_url, - LINKED_REPORT_ID=report_id, - ) - # Hit PowerBiReportServer - LOGGER.info("Request to Report URL={}".format(linked_report_get_endpoint)) - response = requests.get( - url=linked_report_get_endpoint, - auth=self.get_auth_credentials(), - ) - - # Check if we got response from PowerBi Report Server - if response.status_code != 200: - message: str = "Failed to fetch Report from power-bi-report-server for" - LOGGER.warning(message) - LOGGER.warning("{}={}".format(Constant.ReportId, report_id)) - raise ConnectionError(message) - - response_dict = response.json() - - return LinkedReport.parse_obj(response_dict) - - def get_mobile_report(self, report_id: str) -> Optional[MobileReport]: - """ - Fetch the Mobile Report from PowerBiReportServer for the given Report Id - """ - if report_id is None: - LOGGER.info("Input value is None") - LOGGER.info("{}={}".format(Constant.ReportId, report_id)) - return None - - mobile_report_get_endpoint: str = API_ENDPOINTS[Constant.MOBILE_REPORT] - # Replace place holders - mobile_report_get_endpoint = mobile_report_get_endpoint.format( - PBIRS_BASE_URL=self.__config.get_base_api_url, - MOBILE_REPORT_ID=report_id, - ) - # Hit PowerBi ReportServer - LOGGER.info("Request to Report URL={}".format(mobile_report_get_endpoint)) - response = requests.get( - url=mobile_report_get_endpoint, - auth=self.get_auth_credentials(), - ) - # Check if we got response from PowerBi Report Server if response.status_code != 200: message: str = "Failed to fetch Report from power-bi-report-server for" LOGGER.warning(message) - LOGGER.warning("{}={}".format(Constant.ReportId, report_id)) - raise ConnectionError(message) + LOGGER.warning("{}={}".format(Constant.ReportId, content_type)) + raise ValueError(message) - response_dict = response.json() - - return MobileReport.parse_obj(response_dict) + return response.json() def get_all_reports(self) -> List[Any]: """ @@ -299,27 +151,19 @@ def get_all_reports(self) -> List[Any]: reports: List[Any] = [] for report_type in report_types_mapping.keys(): - report_get_endpoint: str = API_ENDPOINTS[report_type] # Replace place holders - report_get_endpoint = report_get_endpoint.format( - PBIRS_BASE_URL=self.__config.get_base_api_url, + report_get_endpoint_http = report_get_endpoint.format( + PBIRS_BASE_URL=self.__config.get_base_api_http_url, ) - # Hit PowerBi ReportServer - LOGGER.info("Request to Report URL={}".format(report_get_endpoint)) - response = requests.get( - url=report_get_endpoint, - auth=self.get_auth_credentials(), + report_get_endpoint_https = report_get_endpoint.format( + PBIRS_BASE_URL=self.__config.get_base_api_https_url, ) - - # Check if we got response from PowerBi Report Server - if response.status_code != 200: - message: str = "Failed to fetch Report from power-bi-report-server for" - LOGGER.warning(message) - LOGGER.warning("{}={}".format(Constant.ReportId, report_type)) - raise ValueError(message) - - response_dict = response.json()["value"] + response_dict = self.requests_get( + url_http=report_get_endpoint_http, + url_https=report_get_endpoint_https, + content_type=report_type, + )["value"] if response_dict: reports.extend( report_types_mapping[report_type].parse_obj(report) @@ -470,7 +314,6 @@ def __to_datahub_dashboard( """ Map PowerBi Report Server report to Datahub Dashboard """ - dashboard_urn = builder.make_dashboard_urn( self.__config.platform_name, report.get_urn_part() ) @@ -650,10 +493,10 @@ def report_dropped(self, view: str) -> None: @capability(SourceCapability.OWNERSHIP, "Enabled by default") class PowerBiReportServerDashboardSource(Source): """ - This plugin extracts the following: + This plugin extracts the following: - - Power BI Dashboards, tiles, datasets - - Names, descriptions and URLs of Dashboard and tile + - Power BI Report Server Dashboards + - Names, descriptions and URLs of Dashboard - Owners of Dashboards ## Configuration Notes @@ -678,7 +521,7 @@ def __init__( super().__init__(ctx) self.source_config = config self.report = PowerBiReportServerDashboardSourceReport() - self.auth = PowerBiReportServerAPI(self.source_config).get_auth_credentials() + self.auth = PowerBiReportServerAPI(self.source_config).get_auth_credentials self.powerbi_client = PowerBiReportServerAPI(self.source_config) self.mapper = Mapper(config) self.user_dao = UserDao(config) @@ -695,7 +538,6 @@ def get_workunits(self) -> Iterable[MetadataWorkUnit]: LOGGER.info("PowerBiReportServer plugin execution is started") # Fetch PowerBiReportServer reports for given url - # workspace = self.powerbi_client.get_workspace(self.source_config.workspace_id) reports = self.powerbi_client.get_all_reports() for report in reports: diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/golden_test_ingest.json b/metadata-ingestion/tests/integration/powerbi_report_server/golden_test_ingest.json index f7f8f285c1c4c..f6c440ac2640f 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/golden_test_ingest.json +++ b/metadata-ingestion/tests/integration/powerbi_report_server/golden_test_ingest.json @@ -1,382 +1,154 @@ [ - { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_book,DEV)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "datasetProperties", - "aspect": { - "value": "{\"customProperties\": {}, \"description\": \"issue_book\", \"tags\": []}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_book,DEV)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.member,DEV)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "datasetProperties", - "aspect": { - "value": "{\"customProperties\": {}, \"description\": \"member\", \"tags\": []}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.member,DEV)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "datasetProperties", - "aspect": { - "value": "{\"customProperties\": {}, \"description\": \"issue_history\", \"tags\": []}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User1@foo.com", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserInfo", - "aspect": { - "value": "{\"active\": true, \"displayName\": \"User1\", \"email\": \"User1@foo.com\", \"title\": \"User1\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User1@foo.com", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User1@foo.com", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserKey", - "aspect": { - "value": "{\"username\": \"User1@foo.com\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User2@foo.com", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserInfo", - "aspect": { - "value": "{\"active\": true, \"displayName\": \"User2\", \"email\": \"User2@foo.com\", \"title\": \"User2\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User2@foo.com", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User2@foo.com", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserKey", - "aspect": { - "value": "{\"username\": \"User2@foo.com\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "chart", - "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "chartInfo", - "aspect": { - "value": "{\"customProperties\": {\"datasetId\": \"05169CD2-E713-41E6-9600-1D8066D95445\", \"reportId\": \"\", \"datasetWebUrl\": \"http://localhost/groups/64ED5CAD-7C10-4684-8180-826122881108/datasets/05169CD2-E713-41E6-9600-1D8066D95445\", \"createdFrom\": \"Dataset\"}, \"title\": \"test_tiles\", \"description\": \"test_tiles\", \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"inputs\": [{\"string\": \"urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_book,DEV)\"}, {\"string\": \"urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.member,DEV)\"}, {\"string\": \"urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)\"}]}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "chart", - "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "chart", - "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "chartKey", - "aspect": { - "value": "{\"dashboardTool\": \"powerbi\", \"chartId\": \"powerbi.linkedin.com/charts/B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "browsePaths", - "aspect": { - "value": "{\"paths\": [\"/powerbi/64ED5CAD-7C10-4684-8180-826122881108\"]}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "dashboardInfo", - "aspect": { - "value": "{\"customProperties\": {\"chartCount\": \"1\", \"workspaceName\": \"foo\", \"workspaceId\": \"7D668CAD-7FFC-4505-9215-655BCA5BEBAE\"}, \"title\": \"test_dashboard\", \"description\": \"test_dashboard\", \"charts\": [\"urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)\"], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"https://localhost/dashboards/web/1\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "dashboardKey", - "aspect": { - "value": "{\"dashboardTool\": \"powerbi\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/7D668CAD-7FFC-4505-9215-655BCA5BEBAE\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "ownership", - "aspect": { - "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:users.User1@foo.com\", \"type\": \"CONSUMER\"}, {\"owner\": \"urn:li:corpuser:users.User2@foo.com\", \"type\": \"CONSUMER\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User1@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserInfo", + "aspect": { + "value": "{\"active\": true, \"displayName\": \"User1\", \"email\": \"User1@foo.com\", \"title\": \"User1\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User1@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserInfo", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User1@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserInfo", + "aspect": { + "value": "{\"username\": \"User1@foo.com\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "browsePaths", + "aspect": { + "value": "{\"paths\": [\"/powerbi_report_server/server_alias/path/to/Test\"]}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "dashboardInfo", + "aspect": { + "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"foo\", \"dataSource\": \"\"}, \"title\": \"Test\", \"description\": \"\", \"charts\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi-report-server/path/to/Test\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "dashboardKey", + "aspect": { + "value": "{\"dashboardTool\": \"powerbi\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938b\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "ownership", + "aspect": { + "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:users.User1@foo.com\", \"type\": \"CONSUMER\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null } - ] \ No newline at end of file +} +] \ No newline at end of file diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py index b50ac1aaaa57a..f6bd32555f238 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py @@ -5,9 +5,9 @@ from freezegun import freeze_time from datahub.ingestion.run.pipeline import Pipeline -from datahub.ingestion.source.powerbi_report_server.report_server_domain import ( +from datahub.ingestion.source.powerbi_report_server import ( + CorpUser, PowerBiReport, - Report, ) from tests.test_helpers import mce_helpers @@ -27,32 +27,11 @@ def test_powerbi_report_server_ingest(pytestconfig, tmp_path, mock_time): # Mock the PowerBi Dashboard API response mocked_client.get_all_reports.return_value = [ PowerBiReport( - Id="c2259cbb-472f-4ee4-897a-97cf24d943db", - Name="6B536811-2E00-4961-9845-8559BA16C122", - Description=None, - Path="/path/to/6B536811-2E00-4961-9845-8559BA16C122", - Type="PowerBIReport", - Hidden=False, - Size=1010101, - ModifiedBy="TEST_USER", - ModifiedDate=datetime.now(), - CreatedBy="TEST_USER", - CreatedDate=datetime.now(), - ParentFolderId="47495172-89ab-455f-a446-fffd3cf239cb", - ContentType=None, - Content="", - IsFavorite=False, - UserInfo=None, - DisplayName="TEST_USER", - HasDataSources=True, - DataSources=[], - ), - Report( Id="ee56dc21-248a-4138-a446-ee5ab1fc938b", - Name="Test_rdl", + Name="Test", Description=None, - Path="/path/to/Test_rdl", - Type="Report", + Path="/path/to/Test", + Type="PowerBIReport", Hidden=False, Size=1010101, ModifiedBy="TEST_USER", @@ -63,12 +42,22 @@ def test_powerbi_report_server_ingest(pytestconfig, tmp_path, mock_time): ContentType=None, Content="", IsFavorite=False, - UserInfo=None, + UserInfo=CorpUser( + active=True, + display_name="User1", + email="User1@foo.com", + title=None, + manager=None, + department_id=None, + department_name=None, + first_name=None, + last_name=None, + full_name=None, + country_code=None, + ), DisplayName="TEST_USER", HasDataSources=True, DataSources=[], - HasSharedDataSets=False, - HasParameters=False, ), ] @@ -84,16 +73,12 @@ def test_powerbi_report_server_ingest(pytestconfig, tmp_path, mock_time): "config": { "username": "foo", "password": "bar", - "workstation_name": "0B0C960B-FCDF-4D0F-8C45-2E03BB59DDEB", - "host_port": "64ED5CAD-7C10-4684-8180-826122881108", - "server_alias": "64ED5CAD", + "workstation_name": "workstation", + "host_port": "host_port", + "server_alias": "server_alias", "graphql_url": "http://localhost:8080/api/graphql", "report_virtual_directory_name": "Reports", "report_server_virtual_directory_name": "ReportServer", - "dataset_type_mapping": { - "PostgreSql": "postgres", - "Oracle": "oracle", - }, "env": "DEV", }, }, From a7589b5ad720869b159d8667a24a9cfff0a73868 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Tue, 2 Aug 2022 12:56:36 +0300 Subject: [PATCH 12/47] ref: change save path directory --- .../source/powerbi_report_server/report_server.py | 11 +++++++++-- .../powerbi_report_server/report_server_domain.py | 8 ++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py index 6c76656e6d491..e4605faebced5 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py @@ -390,7 +390,14 @@ def custom_properties( # Dashboard browsePaths browse_path = BrowsePathsClass( - paths=[report.get_browse_path("powerbi_report_server", self.__config.host)] + paths=[ + report.get_browse_path( + "powerbi_report_server", + self.__config.host, + self.__config.env, + self.__config.report_virtual_directory_name, + ) + ] ) browse_path_mcp = self.new_mcp( entity_type=Constant.DASHBOARD, @@ -409,7 +416,7 @@ def custom_properties( def to_datahub_user(self, user: CorpUser) -> List[MetadataChangeProposalWrapper]: """ - Map PowerBi ReportServer user to datahub user + Map PowerBi Report Server user to datahub user """ LOGGER.info("Converting user {} to datahub's user".format(user.username)) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py index 3416e66d42abd..d36b968d0846e 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py @@ -39,8 +39,12 @@ def get_urn_part(self): def get_web_url(self, base_reports_url: str): return "{}powerbi{}".format(base_reports_url, self.path) - def get_browse_path(self, base_folder: str, workspace: str): - return "/{}/{}{}".format(base_folder, workspace, self.path) + def get_browse_path( + self, base_folder: str, workspace: str, env: str, report_directory: str + ): + return "/{}/{}/{}/{}{}".format( + base_folder, env.lower(), workspace, report_directory, self.path + ) class DataSet(CatalogItem): From ddcea2547e67add6dfddfcf1c187b3afd1c91d08 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Thu, 4 Aug 2022 15:46:39 +0300 Subject: [PATCH 13/47] feat: add test --- .../integration/powerbi_report_server/test.py | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 metadata-ingestion/tests/integration/powerbi_report_server/test.py diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test.py b/metadata-ingestion/tests/integration/powerbi_report_server/test.py new file mode 100644 index 0000000000000..e03c796822b9b --- /dev/null +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test.py @@ -0,0 +1,135 @@ +from datetime import datetime +from unittest import mock + +from freezegun import freeze_time + +from datahub.ingestion.run.pipeline import Pipeline +from tests.test_helpers import mce_helpers +from datahub.ingestion.source.powerbi_report_server import ( + CorpUser, + Constant, +) + + +FROZEN_TIME = "2022-02-03 07:00:00" +POWERBI_REPORT_SERVER_USER_DAO_GET_OWNER = "datahub.ingestion.source.powerbi_report_server.report_server.UserDao.get_owner_by_name" + + +def mock_user(*args, **kwargs): + class UserDao: + def get_owner_by_name(self, *args, **kwargs): + user_data = dict( + urn=f"urn:li:corpuser:User1", + type=Constant.CORP_USER, + username="User1", + properties=dict(active=True, displayName="User1", email="User1@foo.com"), + ) + return CorpUser.parse_obj(user_data) + + return UserDao() + +# +# def mock_ntlm(*args, **kwargs): +# class HttpNtlmAuth: +# def get_owner_by_name(self, *args, **kwargs): +# user_data = dict( +# urn=f"urn:li:corpuser:User1", +# type=Constant.CORP_USER, +# username="User1", +# properties=dict(active=True, displayName="User1", email="User1@foo.com"), +# ) +# return CorpUser.parse_obj(user_data) +# +# return HttpNtlmAuth() + + +def register_mock_api(request_mock): + api_vs_response = { + "http://host_port/Reports/api/v2.0/PowerBIReports(ee56dc21-248a-4138-a446-ee5ab1fc938b)": { + "method": "GET", + "status_code": 200, + "json": { + "value": [ + { + 'Id': 'ee56dc21-248a-4138-a446-ee5ab1fc938b', + 'Name': 'Test', + 'Description': None, + 'Path': '/path/to/Test', + 'Type': 'PowerBIReport', + 'Hidden': False, + 'Size': 1010101, + 'ModifiedBy': 'TEST_USER', + 'ModifiedDate': datetime.now(), + 'CreatedBy': 'TEST_USER', + 'CreatedDate': datetime.now(), + 'ParentFolderId': "47495172-89ab-455f-a446-fffd3cf239cb", + 'IsFavorite': False, + 'ContentType': None, + 'Content': '', + 'HasDataSources': True, + 'Roles': [] + }, + ] + }, + }, + } + + for url in api_vs_response.keys(): + request_mock.register_uri( + api_vs_response[url]["method"], + url, + json=api_vs_response[url]["json"], + status_code=api_vs_response[url]["status_code"], + ) + + +def default_source_config(): + return { + "username": "foo", + "password": "bar", + "workstation_name": "workstation", + "host_port": "host_port", + "server_alias": "server_alias", + "graphql_url": "http://localhost:8080/api/graphql", + "report_virtual_directory_name": "Reports", + "report_server_virtual_directory_name": "ReportServer", + "env": "DEV", + } + + +@freeze_time(FROZEN_TIME) +@mock.patch(POWERBI_REPORT_SERVER_USER_DAO_GET_OWNER, side_effect=mock_user) +# @mock.patch("requests_ntlm.HttpNtlmAuth", side_effect=mock_ntlm) +@mock.patch("requests_ntlm.HttpNtlmAuth") +def test_powerbi_ingest(mock_msal, pytestconfig, tmp_path, mock_time, requests_mock): + test_resources_dir = pytestconfig.rootpath / "tests/integration/powerbi" + + register_mock_api(request_mock=requests_mock) + + pipeline = Pipeline.create( + { + "run_id": "powerbi-report-server-test", + "source": { + "type": "powerbi-report-server", + "config": { + **default_source_config(), + }, + }, + "sink": { + "type": "file", + "config": { + "filename": f"{tmp_path}/powerbi_report_server_mces.json", + }, + }, + } + ) + + pipeline.run() + pipeline.raise_from_status() + mce_out_file = "../powerbi/golden_test_ingest.json" + + mce_helpers.check_golden_file( + pytestconfig, + output_path=tmp_path / "powerbi_report_server_mces.json", + golden_path=f"{test_resources_dir}/{mce_out_file}", + ) From ad0416fc2a752aa05a85ce0b43d80a0dd8569caf Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Thu, 4 Aug 2022 16:03:22 +0300 Subject: [PATCH 14/47] feat: add test --- .../tests/integration/powerbi_report_server/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test.py b/metadata-ingestion/tests/integration/powerbi_report_server/test.py index e03c796822b9b..3879baddfa37b 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test.py +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test.py @@ -101,7 +101,7 @@ def default_source_config(): @mock.patch(POWERBI_REPORT_SERVER_USER_DAO_GET_OWNER, side_effect=mock_user) # @mock.patch("requests_ntlm.HttpNtlmAuth", side_effect=mock_ntlm) @mock.patch("requests_ntlm.HttpNtlmAuth") -def test_powerbi_ingest(mock_msal, pytestconfig, tmp_path, mock_time, requests_mock): +def test_powerbi_ingest(mock_msal, user_mock, pytestconfig, tmp_path, mock_time, requests_mock): test_resources_dir = pytestconfig.rootpath / "tests/integration/powerbi" register_mock_api(request_mock=requests_mock) From 74fca17bcc291ede44314e62985f57122ef0c839 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Tue, 9 Aug 2022 11:00:26 +0300 Subject: [PATCH 15/47] feat: change improts --- .../datahub/ingestion/source/powerbi_report_server/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py index d61be97c5f6ca..a44a97fd60d0f 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py @@ -2,7 +2,8 @@ PowerBiReportServerDashboardSource, ) from datahub.ingestion.source.powerbi_report_server.report_server_domain import ( - CorpUser, PowerBiReport, Report, ) +from datahub.ingestion.source.powerbi_report_server.graphql_domain import CorpUser +from datahub.ingestion.source.powerbi_report_server.constants import Constant \ No newline at end of file From d85196194af600efac0f6f1275689c14d1e25025 Mon Sep 17 00:00:00 2001 From: RChygir <30907647+RChygir@users.noreply.github.com> Date: Tue, 9 Aug 2022 11:52:57 +0300 Subject: [PATCH 16/47] fix: fix powerbi report server tests --- .../powerbi/golden_test_ingest.json | 990 +++++++++++------- .../integration/powerbi_report_server/test.py | 117 ++- 2 files changed, 711 insertions(+), 396 deletions(-) diff --git a/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json b/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json index de37604b3ebbe..adf941f43a9a8 100644 --- a/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json +++ b/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json @@ -1,382 +1,610 @@ [ - { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_book,DEV)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "datasetProperties", - "aspect": { - "value": "{\"customProperties\": {}, \"description\": \"issue_book\", \"tags\": []}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_book,DEV)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.member,DEV)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "datasetProperties", - "aspect": { - "value": "{\"customProperties\": {}, \"description\": \"member\", \"tags\": []}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.member,DEV)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "datasetProperties", - "aspect": { - "value": "{\"customProperties\": {}, \"description\": \"issue_history\", \"tags\": []}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User1@foo.com", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserInfo", - "aspect": { - "value": "{\"active\": true, \"displayName\": \"User1\", \"email\": \"User1@foo.com\", \"title\": \"User1\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User1@foo.com", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User1@foo.com", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserKey", - "aspect": { - "value": "{\"username\": \"User1@foo.com\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User2@foo.com", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserInfo", - "aspect": { - "value": "{\"active\": true, \"displayName\": \"User2\", \"email\": \"User2@foo.com\", \"title\": \"User2\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User2@foo.com", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User2@foo.com", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserKey", - "aspect": { - "value": "{\"username\": \"User2@foo.com\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "chart", - "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "chartInfo", - "aspect": { - "value": "{\"customProperties\": {\"datasetId\": \"05169CD2-E713-41E6-9600-1D8066D95445\", \"reportId\": \"\", \"datasetWebUrl\": \"http://localhost/groups/64ED5CAD-7C10-4684-8180-826122881108/datasets/05169CD2-E713-41E6-9600-1D8066D95445\", \"createdFrom\": \"Dataset\"}, \"title\": \"test_tiles\", \"description\": \"test_tiles\", \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"inputs\": [{\"string\": \"urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_book,DEV)\"}, {\"string\": \"urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.member,DEV)\"}, {\"string\": \"urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)\"}]}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "chart", - "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "chart", - "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "chartKey", - "aspect": { - "value": "{\"dashboardTool\": \"powerbi\", \"chartId\": \"powerbi.linkedin.com/charts/B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "browsePaths", - "aspect": { - "value": "{\"paths\": [\"/powerbi/64ED5CAD-7C10-4684-8180-826122881108\"]}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "dashboardInfo", - "aspect": { - "value": "{\"customProperties\": {\"chartCount\": \"1\", \"workspaceName\": \"foo\", \"workspaceId\": \"7D668CAD-7FFC-4505-9215-655BCA5BEBAE\"}, \"title\": \"test_dashboard\", \"description\": \"test_dashboard\", \"charts\": [\"urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)\"], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"https://localhost/dashboards/web/1\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "dashboardKey", - "aspect": { - "value": "{\"dashboardTool\": \"powerbi\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/7D668CAD-7FFC-4505-9215-655BCA5BEBAE\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "ownership", - "aspect": { - "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:users.User1@foo.com\", \"type\": \"CONSUMER\"}, {\"owner\": \"urn:li:corpuser:users.User2@foo.com\", \"type\": \"CONSUMER\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - } - ] \ No newline at end of file +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:User1", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserInfo", + "aspect": { + "value": "{\"active\": true, \"displayName\": \"User1\", \"email\": \"User1@foo.com\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:User1", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:User1", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserKey", + "aspect": { + "value": "{\"username\": \"User1\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "browsePaths", + "aspect": { + "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Test\"]}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "dashboardInfo", + "aspect": { + "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"dataSource\": \"\"}, \"title\": \"Test\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Test\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "dashboardKey", + "aspect": { + "value": "{\"dashboardTool\": \"powerbi-report-server\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938b\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "ownership", + "aspect": { + "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:User1\", \"type\": \"BUSINESS_OWNER\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:User1", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserInfo", + "aspect": { + "value": "{\"active\": true, \"displayName\": \"User1\", \"email\": \"User1@foo.com\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:User1", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:User1", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserKey", + "aspect": { + "value": "{\"username\": \"User1\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "browsePaths", + "aspect": { + "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Test\"]}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "dashboardInfo", + "aspect": { + "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"dataSource\": \"\"}, \"title\": \"Test\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Test\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "dashboardKey", + "aspect": { + "value": "{\"dashboardTool\": \"powerbi-report-server\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938b\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "ownership", + "aspect": { + "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:User1\", \"type\": \"BUSINESS_OWNER\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:User1", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserInfo", + "aspect": { + "value": "{\"active\": true, \"displayName\": \"User1\", \"email\": \"User1@foo.com\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:User1", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:User1", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserKey", + "aspect": { + "value": "{\"username\": \"User1\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "browsePaths", + "aspect": { + "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Test\"]}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "dashboardInfo", + "aspect": { + "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"dataSource\": \"\"}, \"title\": \"Test\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Test\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "dashboardKey", + "aspect": { + "value": "{\"dashboardTool\": \"powerbi-report-server\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938b\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "ownership", + "aspect": { + "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:User1\", \"type\": \"BUSINESS_OWNER\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:User1", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserInfo", + "aspect": { + "value": "{\"active\": true, \"displayName\": \"User1\", \"email\": \"User1@foo.com\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:User1", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:User1", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserKey", + "aspect": { + "value": "{\"username\": \"User1\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "browsePaths", + "aspect": { + "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Test\"]}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "dashboardInfo", + "aspect": { + "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"dataSource\": \"\"}, \"title\": \"Test\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Test\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "dashboardKey", + "aspect": { + "value": "{\"dashboardTool\": \"powerbi-report-server\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938b\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "ownership", + "aspect": { + "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:User1\", \"type\": \"BUSINESS_OWNER\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +} +] \ No newline at end of file diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test.py b/metadata-ingestion/tests/integration/powerbi_report_server/test.py index 3879baddfa37b..6f9008d421197 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test.py +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test.py @@ -16,17 +16,13 @@ def mock_user(*args, **kwargs): - class UserDao: - def get_owner_by_name(self, *args, **kwargs): - user_data = dict( - urn=f"urn:li:corpuser:User1", - type=Constant.CORP_USER, - username="User1", - properties=dict(active=True, displayName="User1", email="User1@foo.com"), - ) - return CorpUser.parse_obj(user_data) - - return UserDao() + user_data = dict( + urn=f"urn:li:corpuser:User1", + type=Constant.CORP_USER, + username="User1", + properties=dict(active=True, displayName="User1", email="User1@foo.com"), + ) + return CorpUser.parse_obj(user_data) # # def mock_ntlm(*args, **kwargs): @@ -45,7 +41,98 @@ def get_owner_by_name(self, *args, **kwargs): def register_mock_api(request_mock): api_vs_response = { - "http://host_port/Reports/api/v2.0/PowerBIReports(ee56dc21-248a-4138-a446-ee5ab1fc938b)": { + "https://host_port/Reports/api/v2.0/Reports": { + "method": "GET", + "status_code": 200, + "json": { + "value": [ + { + 'Id': 'ee56dc21-248a-4138-a446-ee5ab1fc938b', + 'Name': 'Test', + 'Description': None, + 'Path': '/path/to/Test', + 'Type': 'PowerBIReport', + 'Hidden': False, + 'Size': 1010101, + 'ModifiedBy': 'TEST_USER', + 'ModifiedDate': str(datetime.now()), + 'CreatedBy': 'TEST_USER', + 'CreatedDate': str(datetime.now()), + 'ParentFolderId': "47495172-89ab-455f-a446-fffd3cf239cb", + 'IsFavorite': False, + 'ContentType': None, + 'Content': '', + 'HasDataSources': True, + 'Roles': [], + 'HasSharedDataSets': True, + 'HasParameters': True, + }, + ] + }, + }, + "https://host_port/Reports/api/v2.0/MobileReports": { + "method": "GET", + "status_code": 200, + "json": { + "value": [ + { + 'Id': 'ee56dc21-248a-4138-a446-ee5ab1fc938b', + 'Name': 'Test', + 'Description': None, + 'Path': '/path/to/Test', + 'Type': 'PowerBIReport', + 'Hidden': False, + 'Size': 1010101, + 'ModifiedBy': 'TEST_USER', + 'ModifiedDate': str(datetime.now()), + 'CreatedBy': 'TEST_USER', + 'CreatedDate': str(datetime.now()), + 'ParentFolderId': "47495172-89ab-455f-a446-fffd3cf239cb", + 'IsFavorite': False, + 'ContentType': None, + 'Content': '', + 'HasDataSources': True, + 'Roles': [], + 'HasSharedDataSets': True, + 'HasParameters': True, + 'AllowCaching': True, + 'Manifest': { + 'Resources': [] + } + }, + ] + }, + }, + "https://host_port/Reports/api/v2.0/LinkedReports": { + "method": "GET", + "status_code": 200, + "json": { + "value": [ + { + 'Id': 'ee56dc21-248a-4138-a446-ee5ab1fc938b', + 'Name': 'Test', + 'Description': None, + 'Path': '/path/to/Test', + 'Type': 'PowerBIReport', + 'Hidden': False, + 'Size': 1010101, + 'ModifiedBy': 'TEST_USER', + 'ModifiedDate': str(datetime.now()), + 'CreatedBy': 'TEST_USER', + 'CreatedDate': str(datetime.now()), + 'ParentFolderId': "47495172-89ab-455f-a446-fffd3cf239cb", + 'IsFavorite': False, + 'ContentType': None, + 'Content': '', + 'HasDataSources': True, + 'Roles': [], + 'HasParameters': True, + 'Link': "sjfgnk-7134-1234-abcd-ee5axvcv938b" + }, + ] + }, + }, + "https://host_port/Reports/api/v2.0/PowerBIReports": { "method": "GET", "status_code": 200, "json": { @@ -59,15 +146,15 @@ def register_mock_api(request_mock): 'Hidden': False, 'Size': 1010101, 'ModifiedBy': 'TEST_USER', - 'ModifiedDate': datetime.now(), + 'ModifiedDate': str(datetime.now()), 'CreatedBy': 'TEST_USER', - 'CreatedDate': datetime.now(), + 'CreatedDate': str(datetime.now()), 'ParentFolderId': "47495172-89ab-455f-a446-fffd3cf239cb", 'IsFavorite': False, 'ContentType': None, 'Content': '', 'HasDataSources': True, - 'Roles': [] + 'Roles': [], }, ] }, From 0690f521e8e6805080a8f237985857023e8e4c50 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Tue, 9 Aug 2022 12:09:26 +0300 Subject: [PATCH 17/47] feat: change imports --- .../integration/powerbi_report_server/test.py | 222 -------------- .../test_powerbi_report_server.py | 271 ++++++++++++------ 2 files changed, 186 insertions(+), 307 deletions(-) delete mode 100644 metadata-ingestion/tests/integration/powerbi_report_server/test.py diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test.py b/metadata-ingestion/tests/integration/powerbi_report_server/test.py deleted file mode 100644 index 6f9008d421197..0000000000000 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test.py +++ /dev/null @@ -1,222 +0,0 @@ -from datetime import datetime -from unittest import mock - -from freezegun import freeze_time - -from datahub.ingestion.run.pipeline import Pipeline -from tests.test_helpers import mce_helpers -from datahub.ingestion.source.powerbi_report_server import ( - CorpUser, - Constant, -) - - -FROZEN_TIME = "2022-02-03 07:00:00" -POWERBI_REPORT_SERVER_USER_DAO_GET_OWNER = "datahub.ingestion.source.powerbi_report_server.report_server.UserDao.get_owner_by_name" - - -def mock_user(*args, **kwargs): - user_data = dict( - urn=f"urn:li:corpuser:User1", - type=Constant.CORP_USER, - username="User1", - properties=dict(active=True, displayName="User1", email="User1@foo.com"), - ) - return CorpUser.parse_obj(user_data) - -# -# def mock_ntlm(*args, **kwargs): -# class HttpNtlmAuth: -# def get_owner_by_name(self, *args, **kwargs): -# user_data = dict( -# urn=f"urn:li:corpuser:User1", -# type=Constant.CORP_USER, -# username="User1", -# properties=dict(active=True, displayName="User1", email="User1@foo.com"), -# ) -# return CorpUser.parse_obj(user_data) -# -# return HttpNtlmAuth() - - -def register_mock_api(request_mock): - api_vs_response = { - "https://host_port/Reports/api/v2.0/Reports": { - "method": "GET", - "status_code": 200, - "json": { - "value": [ - { - 'Id': 'ee56dc21-248a-4138-a446-ee5ab1fc938b', - 'Name': 'Test', - 'Description': None, - 'Path': '/path/to/Test', - 'Type': 'PowerBIReport', - 'Hidden': False, - 'Size': 1010101, - 'ModifiedBy': 'TEST_USER', - 'ModifiedDate': str(datetime.now()), - 'CreatedBy': 'TEST_USER', - 'CreatedDate': str(datetime.now()), - 'ParentFolderId': "47495172-89ab-455f-a446-fffd3cf239cb", - 'IsFavorite': False, - 'ContentType': None, - 'Content': '', - 'HasDataSources': True, - 'Roles': [], - 'HasSharedDataSets': True, - 'HasParameters': True, - }, - ] - }, - }, - "https://host_port/Reports/api/v2.0/MobileReports": { - "method": "GET", - "status_code": 200, - "json": { - "value": [ - { - 'Id': 'ee56dc21-248a-4138-a446-ee5ab1fc938b', - 'Name': 'Test', - 'Description': None, - 'Path': '/path/to/Test', - 'Type': 'PowerBIReport', - 'Hidden': False, - 'Size': 1010101, - 'ModifiedBy': 'TEST_USER', - 'ModifiedDate': str(datetime.now()), - 'CreatedBy': 'TEST_USER', - 'CreatedDate': str(datetime.now()), - 'ParentFolderId': "47495172-89ab-455f-a446-fffd3cf239cb", - 'IsFavorite': False, - 'ContentType': None, - 'Content': '', - 'HasDataSources': True, - 'Roles': [], - 'HasSharedDataSets': True, - 'HasParameters': True, - 'AllowCaching': True, - 'Manifest': { - 'Resources': [] - } - }, - ] - }, - }, - "https://host_port/Reports/api/v2.0/LinkedReports": { - "method": "GET", - "status_code": 200, - "json": { - "value": [ - { - 'Id': 'ee56dc21-248a-4138-a446-ee5ab1fc938b', - 'Name': 'Test', - 'Description': None, - 'Path': '/path/to/Test', - 'Type': 'PowerBIReport', - 'Hidden': False, - 'Size': 1010101, - 'ModifiedBy': 'TEST_USER', - 'ModifiedDate': str(datetime.now()), - 'CreatedBy': 'TEST_USER', - 'CreatedDate': str(datetime.now()), - 'ParentFolderId': "47495172-89ab-455f-a446-fffd3cf239cb", - 'IsFavorite': False, - 'ContentType': None, - 'Content': '', - 'HasDataSources': True, - 'Roles': [], - 'HasParameters': True, - 'Link': "sjfgnk-7134-1234-abcd-ee5axvcv938b" - }, - ] - }, - }, - "https://host_port/Reports/api/v2.0/PowerBIReports": { - "method": "GET", - "status_code": 200, - "json": { - "value": [ - { - 'Id': 'ee56dc21-248a-4138-a446-ee5ab1fc938b', - 'Name': 'Test', - 'Description': None, - 'Path': '/path/to/Test', - 'Type': 'PowerBIReport', - 'Hidden': False, - 'Size': 1010101, - 'ModifiedBy': 'TEST_USER', - 'ModifiedDate': str(datetime.now()), - 'CreatedBy': 'TEST_USER', - 'CreatedDate': str(datetime.now()), - 'ParentFolderId': "47495172-89ab-455f-a446-fffd3cf239cb", - 'IsFavorite': False, - 'ContentType': None, - 'Content': '', - 'HasDataSources': True, - 'Roles': [], - }, - ] - }, - }, - } - - for url in api_vs_response.keys(): - request_mock.register_uri( - api_vs_response[url]["method"], - url, - json=api_vs_response[url]["json"], - status_code=api_vs_response[url]["status_code"], - ) - - -def default_source_config(): - return { - "username": "foo", - "password": "bar", - "workstation_name": "workstation", - "host_port": "host_port", - "server_alias": "server_alias", - "graphql_url": "http://localhost:8080/api/graphql", - "report_virtual_directory_name": "Reports", - "report_server_virtual_directory_name": "ReportServer", - "env": "DEV", - } - - -@freeze_time(FROZEN_TIME) -@mock.patch(POWERBI_REPORT_SERVER_USER_DAO_GET_OWNER, side_effect=mock_user) -# @mock.patch("requests_ntlm.HttpNtlmAuth", side_effect=mock_ntlm) -@mock.patch("requests_ntlm.HttpNtlmAuth") -def test_powerbi_ingest(mock_msal, user_mock, pytestconfig, tmp_path, mock_time, requests_mock): - test_resources_dir = pytestconfig.rootpath / "tests/integration/powerbi" - - register_mock_api(request_mock=requests_mock) - - pipeline = Pipeline.create( - { - "run_id": "powerbi-report-server-test", - "source": { - "type": "powerbi-report-server", - "config": { - **default_source_config(), - }, - }, - "sink": { - "type": "file", - "config": { - "filename": f"{tmp_path}/powerbi_report_server_mces.json", - }, - }, - } - ) - - pipeline.run() - pipeline.raise_from_status() - mce_out_file = "../powerbi/golden_test_ingest.json" - - mce_helpers.check_golden_file( - pytestconfig, - output_path=tmp_path / "powerbi_report_server_mces.json", - golden_path=f"{test_resources_dir}/{mce_out_file}", - ) diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py index f6bd32555f238..cd63588f62399 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py @@ -1,102 +1,203 @@ from datetime import datetime from unittest import mock -from unittest.mock import patch from freezegun import freeze_time from datahub.ingestion.run.pipeline import Pipeline -from datahub.ingestion.source.powerbi_report_server import ( - CorpUser, - PowerBiReport, -) +from datahub.ingestion.source.powerbi_report_server import Constant, CorpUser from tests.test_helpers import mce_helpers -FROZEN_TIME = "2022-07-08 07:00:00" - -POWERBI_REPORT_SERVER_API = "datahub.ingestion.source.powerbi_report_server.report_server.PowerBiReportServerAPI" +FROZEN_TIME = "2022-02-03 07:00:00" POWERBI_REPORT_SERVER_USER_DAO_GET_OWNER = "datahub.ingestion.source.powerbi_report_server.report_server.UserDao.get_owner_by_name" -@freeze_time(FROZEN_TIME) -@patch(POWERBI_REPORT_SERVER_USER_DAO_GET_OWNER) -def test_powerbi_report_server_ingest(pytestconfig, tmp_path, mock_time): - mocked_client = mock.MagicMock() - # Set the datasource mapping dictionary - with mock.patch(POWERBI_REPORT_SERVER_API) as mock_sdk: - mock_sdk.return_value = mocked_client - # Mock the PowerBi Dashboard API response - mocked_client.get_all_reports.return_value = [ - PowerBiReport( - Id="ee56dc21-248a-4138-a446-ee5ab1fc938b", - Name="Test", - Description=None, - Path="/path/to/Test", - Type="PowerBIReport", - Hidden=False, - Size=1010101, - ModifiedBy="TEST_USER", - ModifiedDate=datetime.now(), - CreatedBy="TEST_USER", - CreatedDate=datetime.now(), - ParentFolderId="47495172-89ab-455f-a446-fffd3cf239cb", - ContentType=None, - Content="", - IsFavorite=False, - UserInfo=CorpUser( - active=True, - display_name="User1", - email="User1@foo.com", - title=None, - manager=None, - department_id=None, - department_name=None, - first_name=None, - last_name=None, - full_name=None, - country_code=None, - ), - DisplayName="TEST_USER", - HasDataSources=True, - DataSources=[], - ), - ] - - test_resources_dir = ( - pytestconfig.rootpath / "tests/integration/powerbi_report_server" - ) +def mock_user(*args, **kwargs): + user_data = dict( + urn=f"urn:li:corpuser:User1", + type=Constant.CORP_USER, + username="User1", + properties=dict(active=True, displayName="User1", email="User1@foo.com"), + ) + return CorpUser.parse_obj(user_data) - pipeline = Pipeline.create( - { - "run_id": "powerbi-report-server-test", - "source": { - "type": "powerbi-report-server", - "config": { - "username": "foo", - "password": "bar", - "workstation_name": "workstation", - "host_port": "host_port", - "server_alias": "server_alias", - "graphql_url": "http://localhost:8080/api/graphql", - "report_virtual_directory_name": "Reports", - "report_server_virtual_directory_name": "ReportServer", - "env": "DEV", + +def register_mock_api(request_mock): + api_vs_response = { + "https://host_port/Reports/api/v2.0/Reports": { + "method": "GET", + "status_code": 200, + "json": { + "value": [ + { + "Id": "ee56dc21-248a-4138-a446-ee5ab1fc938a", + "Name": "Testa", + "Description": None, + "Path": "/path/to/Testa", + "Type": "Report", + "Hidden": False, + "Size": 1010101, + "ModifiedBy": "TEST_USER", + "ModifiedDate": str(datetime.now()), + "CreatedBy": "TEST_USER", + "CreatedDate": str(datetime.now()), + "ParentFolderId": "47495172-89ab-455f-a446-fffd3cf239ca", + "IsFavorite": False, + "ContentType": None, + "Content": "", + "HasDataSources": True, + "Roles": [], + "HasSharedDataSets": True, + "HasParameters": True, }, - }, - "sink": { - "type": "file", - "config": { - "filename": f"{tmp_path}/powerbi_report_server_mces.json", + ] + }, + }, + "https://host_port/Reports/api/v2.0/MobileReports": { + "method": "GET", + "status_code": 200, + "json": { + "value": [ + { + "Id": "ee56dc21-248a-4138-a446-ee5ab1fc938b", + "Name": "Testb", + "Description": None, + "Path": "/path/to/Testb", + "Type": "MobileReport", + "Hidden": False, + "Size": 1010101, + "ModifiedBy": "TEST_USER", + "ModifiedDate": str(datetime.now()), + "CreatedBy": "TEST_USER", + "CreatedDate": str(datetime.now()), + "ParentFolderId": "47495172-89ab-455f-a446-fffd3cf239cb", + "IsFavorite": False, + "ContentType": None, + "Content": "", + "HasDataSources": True, + "Roles": [], + "HasSharedDataSets": True, + "HasParameters": True, + "AllowCaching": True, + "Manifest": {"Resources": []}, }, - }, - } + ] + }, + }, + "https://host_port/Reports/api/v2.0/LinkedReports": { + "method": "GET", + "status_code": 200, + "json": { + "value": [ + { + "Id": "ee56dc21-248a-4138-a446-ee5ab1fc938c", + "Name": "Testc", + "Description": None, + "Path": "/path/to/Testc", + "Type": "LinkedReport", + "Hidden": False, + "Size": 1010101, + "ModifiedBy": "TEST_USER", + "ModifiedDate": str(datetime.now()), + "CreatedBy": "TEST_USER", + "CreatedDate": str(datetime.now()), + "ParentFolderId": "47495172-89ab-455f-a446-fffd3cf239cc", + "IsFavorite": False, + "ContentType": None, + "Content": "", + "HasDataSources": True, + "Roles": [], + "HasParameters": True, + "Link": "sjfgnk-7134-1234-abcd-ee5axvcv938b", + }, + ] + }, + }, + "https://host_port/Reports/api/v2.0/PowerBIReports": { + "method": "GET", + "status_code": 200, + "json": { + "value": [ + { + "Id": "ee56dc21-248a-4138-a446-ee5ab1fc938d", + "Name": "Testd", + "Description": None, + "Path": "/path/to/Testd", + "Type": "PowerBIReport", + "Hidden": False, + "Size": 1010101, + "ModifiedBy": "TEST_USER", + "ModifiedDate": str(datetime.now()), + "CreatedBy": "TEST_USER", + "CreatedDate": str(datetime.now()), + "ParentFolderId": "47495172-89ab-455f-a446-fffd3cf239cd", + "IsFavorite": False, + "ContentType": None, + "Content": "", + "HasDataSources": True, + "Roles": [], + }, + ] + }, + }, + } + + for url in api_vs_response.keys(): + request_mock.register_uri( + api_vs_response[url]["method"], + url, + json=api_vs_response[url]["json"], + status_code=api_vs_response[url]["status_code"], ) - pipeline.run() - pipeline.raise_from_status() - mce_out_file = "golden_test_ingest.json" - mce_helpers.check_golden_file( - pytestconfig, - output_path=tmp_path / "powerbi_report_server_mces.json", - golden_path=f"{test_resources_dir}/{mce_out_file}", - ) +def default_source_config(): + return { + "username": "foo", + "password": "bar", + "workstation_name": "workstation", + "host_port": "host_port", + "server_alias": "server_alias", + "graphql_url": "http://localhost:8080/api/graphql", + "report_virtual_directory_name": "Reports", + "report_server_virtual_directory_name": "ReportServer", + "env": "DEV", + } + + +@freeze_time(FROZEN_TIME) +@mock.patch(POWERBI_REPORT_SERVER_USER_DAO_GET_OWNER, side_effect=mock_user) +@mock.patch("requests_ntlm.HttpNtlmAuth") +def test_powerbi_ingest( + mock_msal, user_mock, pytestconfig, tmp_path, mock_time, requests_mock +): + test_resources_dir = pytestconfig.rootpath / "tests/integration/powerbi" + + register_mock_api(request_mock=requests_mock) + + pipeline = Pipeline.create( + { + "run_id": "powerbi-report-server-test", + "source": { + "type": "powerbi-report-server", + "config": { + **default_source_config(), + }, + }, + "sink": { + "type": "file", + "config": { + "filename": f"{tmp_path}/powerbi_report_server_mces.json", + }, + }, + } + ) + + pipeline.run() + pipeline.raise_from_status() + mce_out_file = "../powerbi/golden_test_ingest.json" + + mce_helpers.check_golden_file( + pytestconfig, + output_path=tmp_path / "powerbi_report_server_mces.json", + golden_path=f"{test_resources_dir}/{mce_out_file}", + ) From 52e6cd276c7ac86fda3398467bb3ce48a1c30d02 Mon Sep 17 00:00:00 2001 From: RChygir <30907647+RChygir@users.noreply.github.com> Date: Tue, 9 Aug 2022 12:26:21 +0300 Subject: [PATCH 18/47] fix: update golden file --- .../powerbi/golden_test_ingest.json | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json b/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json index adf941f43a9a8..7dadb833681a1 100644 --- a/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json +++ b/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json @@ -59,12 +59,12 @@ { "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938a)", "entityKeyAspect": null, "changeType": "UPSERT", "aspectName": "browsePaths", "aspect": { - "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Test\"]}", + "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Testa\"]}", "contentType": "application/json" }, "systemMetadata": { @@ -78,12 +78,12 @@ { "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938a)", "entityKeyAspect": null, "changeType": "UPSERT", "aspectName": "dashboardInfo", "aspect": { - "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"dataSource\": \"\"}, \"title\": \"Test\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Test\"}", + "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"dataSource\": \"\"}, \"title\": \"Testa\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Testa\"}", "contentType": "application/json" }, "systemMetadata": { @@ -97,7 +97,7 @@ { "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938a)", "entityKeyAspect": null, "changeType": "UPSERT", "aspectName": "status", @@ -116,12 +116,12 @@ { "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938a)", "entityKeyAspect": null, "changeType": "UPSERT", "aspectName": "dashboardKey", "aspect": { - "value": "{\"dashboardTool\": \"powerbi-report-server\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938b\"}", + "value": "{\"dashboardTool\": \"powerbi-report-server\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938a\"}", "contentType": "application/json" }, "systemMetadata": { @@ -135,7 +135,7 @@ { "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938a)", "entityKeyAspect": null, "changeType": "UPSERT", "aspectName": "ownership", @@ -216,7 +216,7 @@ "changeType": "UPSERT", "aspectName": "browsePaths", "aspect": { - "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Test\"]}", + "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Testb\"]}", "contentType": "application/json" }, "systemMetadata": { @@ -235,7 +235,7 @@ "changeType": "UPSERT", "aspectName": "dashboardInfo", "aspect": { - "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"dataSource\": \"\"}, \"title\": \"Test\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Test\"}", + "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"dataSource\": \"\"}, \"title\": \"Testb\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Testb\"}", "contentType": "application/json" }, "systemMetadata": { @@ -363,12 +363,12 @@ { "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938c)", "entityKeyAspect": null, "changeType": "UPSERT", "aspectName": "browsePaths", "aspect": { - "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Test\"]}", + "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Testc\"]}", "contentType": "application/json" }, "systemMetadata": { @@ -382,12 +382,12 @@ { "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938c)", "entityKeyAspect": null, "changeType": "UPSERT", "aspectName": "dashboardInfo", "aspect": { - "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"dataSource\": \"\"}, \"title\": \"Test\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Test\"}", + "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"dataSource\": \"\"}, \"title\": \"Testc\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Testc\"}", "contentType": "application/json" }, "systemMetadata": { @@ -401,7 +401,7 @@ { "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938c)", "entityKeyAspect": null, "changeType": "UPSERT", "aspectName": "status", @@ -420,12 +420,12 @@ { "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938c)", "entityKeyAspect": null, "changeType": "UPSERT", "aspectName": "dashboardKey", "aspect": { - "value": "{\"dashboardTool\": \"powerbi-report-server\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938b\"}", + "value": "{\"dashboardTool\": \"powerbi-report-server\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938c\"}", "contentType": "application/json" }, "systemMetadata": { @@ -439,7 +439,7 @@ { "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938c)", "entityKeyAspect": null, "changeType": "UPSERT", "aspectName": "ownership", @@ -515,12 +515,12 @@ { "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938d)", "entityKeyAspect": null, "changeType": "UPSERT", "aspectName": "browsePaths", "aspect": { - "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Test\"]}", + "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Testd\"]}", "contentType": "application/json" }, "systemMetadata": { @@ -534,12 +534,12 @@ { "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938d)", "entityKeyAspect": null, "changeType": "UPSERT", "aspectName": "dashboardInfo", "aspect": { - "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"dataSource\": \"\"}, \"title\": \"Test\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Test\"}", + "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"dataSource\": \"\"}, \"title\": \"Testd\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Testd\"}", "contentType": "application/json" }, "systemMetadata": { @@ -553,7 +553,7 @@ { "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938d)", "entityKeyAspect": null, "changeType": "UPSERT", "aspectName": "status", @@ -572,12 +572,12 @@ { "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938d)", "entityKeyAspect": null, "changeType": "UPSERT", "aspectName": "dashboardKey", "aspect": { - "value": "{\"dashboardTool\": \"powerbi-report-server\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938b\"}", + "value": "{\"dashboardTool\": \"powerbi-report-server\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938d\"}", "contentType": "application/json" }, "systemMetadata": { @@ -591,7 +591,7 @@ { "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938d)", "entityKeyAspect": null, "changeType": "UPSERT", "aspectName": "ownership", From 052a2f77e76829d29ca7eec1313925e6cd6ab162 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Tue, 9 Aug 2022 12:31:51 +0300 Subject: [PATCH 19/47] feat: change json --- .../powerbi/golden_test_ingest.json | 990 +++++++----------- 1 file changed, 381 insertions(+), 609 deletions(-) diff --git a/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json b/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json index 7dadb833681a1..f7f8f285c1c4c 100644 --- a/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json +++ b/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json @@ -1,610 +1,382 @@ [ -{ - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:User1", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserInfo", - "aspect": { - "value": "{\"active\": true, \"displayName\": \"User1\", \"email\": \"User1@foo.com\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:User1", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:User1", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserKey", - "aspect": { - "value": "{\"username\": \"User1\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938a)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "browsePaths", - "aspect": { - "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Testa\"]}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938a)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "dashboardInfo", - "aspect": { - "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"dataSource\": \"\"}, \"title\": \"Testa\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Testa\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938a)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938a)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "dashboardKey", - "aspect": { - "value": "{\"dashboardTool\": \"powerbi-report-server\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938a\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938a)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "ownership", - "aspect": { - "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:User1\", \"type\": \"BUSINESS_OWNER\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:User1", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserInfo", - "aspect": { - "value": "{\"active\": true, \"displayName\": \"User1\", \"email\": \"User1@foo.com\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:User1", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:User1", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserKey", - "aspect": { - "value": "{\"username\": \"User1\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "browsePaths", - "aspect": { - "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Testb\"]}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "dashboardInfo", - "aspect": { - "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"dataSource\": \"\"}, \"title\": \"Testb\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Testb\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "dashboardKey", - "aspect": { - "value": "{\"dashboardTool\": \"powerbi-report-server\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938b\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "ownership", - "aspect": { - "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:User1\", \"type\": \"BUSINESS_OWNER\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:User1", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserInfo", - "aspect": { - "value": "{\"active\": true, \"displayName\": \"User1\", \"email\": \"User1@foo.com\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:User1", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:User1", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserKey", - "aspect": { - "value": "{\"username\": \"User1\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938c)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "browsePaths", - "aspect": { - "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Testc\"]}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938c)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "dashboardInfo", - "aspect": { - "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"dataSource\": \"\"}, \"title\": \"Testc\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Testc\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938c)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938c)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "dashboardKey", - "aspect": { - "value": "{\"dashboardTool\": \"powerbi-report-server\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938c\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938c)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "ownership", - "aspect": { - "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:User1\", \"type\": \"BUSINESS_OWNER\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:User1", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserInfo", - "aspect": { - "value": "{\"active\": true, \"displayName\": \"User1\", \"email\": \"User1@foo.com\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:User1", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:User1", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserKey", - "aspect": { - "value": "{\"username\": \"User1\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938d)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "browsePaths", - "aspect": { - "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Testd\"]}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938d)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "dashboardInfo", - "aspect": { - "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"dataSource\": \"\"}, \"title\": \"Testd\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Testd\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938d)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938d)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "dashboardKey", - "aspect": { - "value": "{\"dashboardTool\": \"powerbi-report-server\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938d\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -}, -{ - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938d)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "ownership", - "aspect": { - "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:User1\", \"type\": \"BUSINESS_OWNER\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-report-server-test", - "registryName": null, - "registryVersion": null, - "properties": null - } -} -] \ No newline at end of file + { + "auditHeader": null, + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_book,DEV)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "datasetProperties", + "aspect": { + "value": "{\"customProperties\": {}, \"description\": \"issue_book\", \"tags\": []}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_book,DEV)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.member,DEV)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "datasetProperties", + "aspect": { + "value": "{\"customProperties\": {}, \"description\": \"member\", \"tags\": []}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.member,DEV)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "datasetProperties", + "aspect": { + "value": "{\"customProperties\": {}, \"description\": \"issue_history\", \"tags\": []}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User1@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserInfo", + "aspect": { + "value": "{\"active\": true, \"displayName\": \"User1\", \"email\": \"User1@foo.com\", \"title\": \"User1\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User1@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User1@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserKey", + "aspect": { + "value": "{\"username\": \"User1@foo.com\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User2@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserInfo", + "aspect": { + "value": "{\"active\": true, \"displayName\": \"User2\", \"email\": \"User2@foo.com\", \"title\": \"User2\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User2@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User2@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserKey", + "aspect": { + "value": "{\"username\": \"User2@foo.com\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "chart", + "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "chartInfo", + "aspect": { + "value": "{\"customProperties\": {\"datasetId\": \"05169CD2-E713-41E6-9600-1D8066D95445\", \"reportId\": \"\", \"datasetWebUrl\": \"http://localhost/groups/64ED5CAD-7C10-4684-8180-826122881108/datasets/05169CD2-E713-41E6-9600-1D8066D95445\", \"createdFrom\": \"Dataset\"}, \"title\": \"test_tiles\", \"description\": \"test_tiles\", \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"inputs\": [{\"string\": \"urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_book,DEV)\"}, {\"string\": \"urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.member,DEV)\"}, {\"string\": \"urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)\"}]}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "chart", + "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "chart", + "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "chartKey", + "aspect": { + "value": "{\"dashboardTool\": \"powerbi\", \"chartId\": \"powerbi.linkedin.com/charts/B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "browsePaths", + "aspect": { + "value": "{\"paths\": [\"/powerbi/64ED5CAD-7C10-4684-8180-826122881108\"]}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "dashboardInfo", + "aspect": { + "value": "{\"customProperties\": {\"chartCount\": \"1\", \"workspaceName\": \"foo\", \"workspaceId\": \"7D668CAD-7FFC-4505-9215-655BCA5BEBAE\"}, \"title\": \"test_dashboard\", \"description\": \"test_dashboard\", \"charts\": [\"urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)\"], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"https://localhost/dashboards/web/1\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "dashboardKey", + "aspect": { + "value": "{\"dashboardTool\": \"powerbi\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/7D668CAD-7FFC-4505-9215-655BCA5BEBAE\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + }, + { + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "ownership", + "aspect": { + "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:users.User1@foo.com\", \"type\": \"CONSUMER\"}, {\"owner\": \"urn:li:corpuser:users.User2@foo.com\", \"type\": \"CONSUMER\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } + } + ] \ No newline at end of file From d8065c6779fc4009264190e141d675b986a8486d Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Tue, 9 Aug 2022 12:33:02 +0300 Subject: [PATCH 20/47] feat: change json --- .../powerbi/golden_test_ingest.json | 38 ------------------- 1 file changed, 38 deletions(-) diff --git a/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json b/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json index f7f8f285c1c4c..a20c5f7efef5e 100644 --- a/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json +++ b/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json @@ -246,44 +246,6 @@ "properties": null } }, - { - "auditHeader": null, - "entityType": "chart", - "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "chart", - "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "chartKey", - "aspect": { - "value": "{\"dashboardTool\": \"powerbi\", \"chartId\": \"powerbi.linkedin.com/charts/B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, { "auditHeader": null, "entityType": "dashboard", From f9891391d9db9a02ee2921007e97641b4bce5940 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Tue, 9 Aug 2022 12:34:25 +0300 Subject: [PATCH 21/47] fix: change json --- .../powerbi/golden_test_ingest.json | 614 ++++++++---------- 1 file changed, 288 insertions(+), 326 deletions(-) diff --git a/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json b/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json index a20c5f7efef5e..62ec08afd2f9a 100644 --- a/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json +++ b/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json @@ -1,344 +1,306 @@ [ - { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_book,DEV)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "datasetProperties", - "aspect": { - "value": "{\"customProperties\": {}, \"description\": \"issue_book\", \"tags\": []}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } +{ + "auditHeader": null, + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "datasetProperties", + "aspect": { + "value": "{\"customProperties\": {}, \"description\": \"issue_history\", \"tags\": []}", + "contentType": "application/json" }, - { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_book,DEV)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } - }, - { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.member,DEV)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "datasetProperties", - "aspect": { - "value": "{\"customProperties\": {}, \"description\": \"member\", \"tags\": []}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" }, - { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.member,DEV)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User1@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserInfo", + "aspect": { + "value": "{\"active\": true, \"displayName\": \"user1\", \"email\": \"User1@foo.com\", \"title\": \"user1\"}", + "contentType": "application/json" }, - { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "datasetProperties", - "aspect": { - "value": "{\"customProperties\": {}, \"description\": \"issue_history\", \"tags\": []}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User1@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" }, - { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User1@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserKey", + "aspect": { + "value": "{\"username\": \"User1@foo.com\"}", + "contentType": "application/json" }, - { - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User1@foo.com", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserInfo", - "aspect": { - "value": "{\"active\": true, \"displayName\": \"User1\", \"email\": \"User1@foo.com\", \"title\": \"User1\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User2@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserInfo", + "aspect": { + "value": "{\"active\": true, \"displayName\": \"user2\", \"email\": \"User2@foo.com\", \"title\": \"user2\"}", + "contentType": "application/json" }, - { - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User1@foo.com", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User2@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" }, - { - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User1@foo.com", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserKey", - "aspect": { - "value": "{\"username\": \"User1@foo.com\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:users.User2@foo.com", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "corpUserKey", + "aspect": { + "value": "{\"username\": \"User2@foo.com\"}", + "contentType": "application/json" }, - { - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User2@foo.com", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserInfo", - "aspect": { - "value": "{\"active\": true, \"displayName\": \"User2\", \"email\": \"User2@foo.com\", \"title\": \"User2\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "chart", + "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "chartInfo", + "aspect": { + "value": "{\"customProperties\": {\"datasetId\": \"05169CD2-E713-41E6-9600-1D8066D95445\", \"reportId\": \"\", \"datasetWebUrl\": \"http://localhost/groups/64ED5CAD-7C10-4684-8180-826122881108/datasets/05169CD2-E713-41E6-9600-1D8066D95445/details\", \"createdFrom\": \"Dataset\"}, \"title\": \"test_tile\", \"description\": \"test_tile\", \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"inputs\": [{\"string\": \"urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)\"}]}", + "contentType": "application/json" }, - { - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User2@foo.com", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "chart", + "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" }, - { - "auditHeader": null, - "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User2@foo.com", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "corpUserKey", - "aspect": { - "value": "{\"username\": \"User2@foo.com\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "chart", + "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "chartKey", + "aspect": { + "value": "{\"dashboardTool\": \"powerbi\", \"chartId\": \"powerbi.linkedin.com/charts/B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0\"}", + "contentType": "application/json" }, - { - "auditHeader": null, - "entityType": "chart", - "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "chartInfo", - "aspect": { - "value": "{\"customProperties\": {\"datasetId\": \"05169CD2-E713-41E6-9600-1D8066D95445\", \"reportId\": \"\", \"datasetWebUrl\": \"http://localhost/groups/64ED5CAD-7C10-4684-8180-826122881108/datasets/05169CD2-E713-41E6-9600-1D8066D95445\", \"createdFrom\": \"Dataset\"}, \"title\": \"test_tiles\", \"description\": \"test_tiles\", \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"inputs\": [{\"string\": \"urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_book,DEV)\"}, {\"string\": \"urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.member,DEV)\"}, {\"string\": \"urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)\"}]}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "browsePaths", + "aspect": { + "value": "{\"paths\": [\"/powerbi/64ED5CAD-7C10-4684-8180-826122881108\"]}", + "contentType": "application/json" }, - { - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "browsePaths", - "aspect": { - "value": "{\"paths\": [\"/powerbi/64ED5CAD-7C10-4684-8180-826122881108\"]}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "dashboardInfo", + "aspect": { + "value": "{\"customProperties\": {\"chartCount\": \"1\", \"workspaceName\": \"demo-workspace\", \"workspaceId\": \"7D668CAD-7FFC-4505-9215-655BCA5BEBAE\"}, \"title\": \"test_dashboard\", \"description\": \"test_dashboard\", \"charts\": [\"urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)\"], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"https://localhost/dashboards/web/1\"}", + "contentType": "application/json" }, - { - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "dashboardInfo", - "aspect": { - "value": "{\"customProperties\": {\"chartCount\": \"1\", \"workspaceName\": \"foo\", \"workspaceId\": \"7D668CAD-7FFC-4505-9215-655BCA5BEBAE\"}, \"title\": \"test_dashboard\", \"description\": \"test_dashboard\", \"charts\": [\"urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)\"], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"https://localhost/dashboards/web/1\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" }, - { - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "status", - "aspect": { - "value": "{\"removed\": false}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "dashboardKey", + "aspect": { + "value": "{\"dashboardTool\": \"powerbi\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/7D668CAD-7FFC-4505-9215-655BCA5BEBAE\"}", + "contentType": "application/json" }, - { - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "dashboardKey", - "aspect": { - "value": "{\"dashboardTool\": \"powerbi\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/7D668CAD-7FFC-4505-9215-655BCA5BEBAE\"}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null + } +}, +{ + "auditHeader": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", + "entityKeyAspect": null, + "changeType": "UPSERT", + "aspectName": "ownership", + "aspect": { + "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:users.User1@foo.com\", \"type\": \"NONE\"}, {\"owner\": \"urn:li:corpuser:users.User2@foo.com\", \"type\": \"NONE\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", + "contentType": "application/json" }, - { - "auditHeader": null, - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, - "changeType": "UPSERT", - "aspectName": "ownership", - "aspect": { - "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:users.User1@foo.com\", \"type\": \"CONSUMER\"}, {\"owner\": \"urn:li:corpuser:users.User2@foo.com\", \"type\": \"CONSUMER\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", - "contentType": "application/json" - }, - "systemMetadata": { - "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null - } + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-test", + "registryName": null, + "registryVersion": null, + "properties": null } - ] \ No newline at end of file +} +] \ No newline at end of file From a6b95a5414f3f9abe107c1e6606898af4c3b1afd Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Wed, 10 Aug 2022 09:38:05 +0300 Subject: [PATCH 22/47] fix: change order in setup --- metadata-ingestion/setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/metadata-ingestion/setup.py b/metadata-ingestion/setup.py index 473c0cbf01c78..9fc1d1206a729 100644 --- a/metadata-ingestion/setup.py +++ b/metadata-ingestion/setup.py @@ -317,8 +317,9 @@ def get_long_description(): "starburst-trino-usage": sql_common | usage_common | trino, "nifi": {"requests", "packaging"}, "powerbi": {"orderedset"} | microsoft_common, - "powerbi-report-server": {"orderedset"} | powerbi_report_server, "vertica": sql_common | {"sqlalchemy-vertica[vertica-python]==0.0.5"}, + "powerbi-report-server": {"orderedset"} | powerbi_report_server, + } all_exclude_plugins: Set[str] = { From f9b6d735ca637b9ca82d9707c6d1339a56d80c86 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Wed, 10 Aug 2022 11:54:34 +0300 Subject: [PATCH 23/47] fix: change pydantic import --- .../powerbi_report_server/report_server.py | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py index e4605faebced5..c72cb1df278bd 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py @@ -9,9 +9,8 @@ from typing import Any, Dict, Iterable, List, Optional, Set import requests +import pydantic from orderedset import OrderedSet -from pydantic import ValidationError -from pydantic.fields import Field from requests.exceptions import ConnectionError from requests_ntlm import HttpNtlmAuth @@ -52,21 +51,21 @@ class PowerBiReportServerAPIConfig(EnvBasedSourceConfigBase): - username: str = Field(description="Windows account username") - password: str = Field(description="Windows account password") - workstation_name: str = Field(default="localhost", description="Workstation name") - host_port: str = Field(description="Power BI Report Server host URL") - server_alias: str = Field( + username: str = pydantic.Field(description="Windows account username") + password: str = pydantic.Field(description="Windows account password") + workstation_name: str = pydantic.Field(default="localhost", description="Workstation name") + host_port: str = pydantic.Field(description="Power BI Report Server host URL") + server_alias: str = pydantic.Field( default="", description="Alias for Power BI Report Server host URL" ) - graphql_url: str = Field(description="GraphQL API URL") - report_virtual_directory_name: str = Field( + graphql_url: str = pydantic.Field(description="GraphQL API URL") + report_virtual_directory_name: str = pydantic.Field( description="Report Virtual Directory URL name" ) - report_server_virtual_directory_name: str = Field( + report_server_virtual_directory_name: str = pydantic.Field( description="Report Server Virtual Directory URL name" ) - scan_timeout: int = Field( + scan_timeout: int = pydantic.Field( default=60, description="time in seconds to wait for Power BI metadata scan result.", ) @@ -552,7 +551,7 @@ def get_workunits(self) -> Iterable[MetadataWorkUnit]: report.user_info = self.user_dao.get_owner_by_name( user_name=report.display_name ) - except ValidationError as e: + except pydantic.ValidationError as e: message = "Error ({}) occurred while loading User {}(id={})".format( e, report.name, report.id ) From 6c9747d93cb1e909398fb8fb73f401f18b2a5f5e Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Wed, 10 Aug 2022 13:43:30 +0300 Subject: [PATCH 24/47] fix: change docs --- ...{powerbi_report_server.md => powerbi-report-server.md} | 8 +++----- ...server_recipe.yml => powerbi-report-server_recipe.yml} | 0 2 files changed, 3 insertions(+), 5 deletions(-) rename metadata-ingestion/docs/sources/powerbi-report-server/{powerbi_report_server.md => powerbi-report-server.md} (69%) rename metadata-ingestion/docs/sources/powerbi-report-server/{powerbi_report_server_recipe.yml => powerbi-report-server_recipe.yml} (100%) diff --git a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server.md b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server.md similarity index 69% rename from metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server.md rename to metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server.md index f7afccb5169eb..33133b39e5437 100644 --- a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server.md +++ b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server.md @@ -1,4 +1,6 @@ -# PowerBI Report Server +## Configuration Notes +See the +[Microsoft AD App Creation doc](https://docs.microsoft.com/en-us/sql/reporting-services/security/grant-user-access-to-a-report-server?view=sql-server-ver16) ## Concept mapping @@ -9,7 +11,3 @@ | `Mobile Report` | `Dashboard` | | `Linked Report` | `Dashboard` | | `Dataset, Datasource` | `N/A` | - -### Note - -Current [PowerBI](https://powerbi.microsoft.com/en-us/what-is-power-bi/) module used only for work with [PowerBI Report Server](https://powerbi.microsoft.com/en-us/report-server/). diff --git a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server_recipe.yml b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server_recipe.yml similarity index 100% rename from metadata-ingestion/docs/sources/powerbi-report-server/powerbi_report_server_recipe.yml rename to metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server_recipe.yml From 07b1fdb2a25b8c5885190f12b30a874ce2ffcba6 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Wed, 10 Aug 2022 14:00:08 +0300 Subject: [PATCH 25/47] fix: fix platform name --- .../powerbi-report-server/powerbi-report-server_recipe.yml | 4 +++- .../ingestion/source/powerbi_report_server/report_server.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server_recipe.yml b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server_recipe.yml index 9c144e1687f6f..72bfce7be1028 100644 --- a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server_recipe.yml +++ b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server_recipe.yml @@ -20,5 +20,7 @@ source: # Your Power BI Report Server base virtual directory name for report server report_server_virtual_directory_name: ReportServer + + sink: - # sink configs + # sink configs \ No newline at end of file diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py index c72cb1df278bd..78a5795856adb 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py @@ -493,7 +493,7 @@ def report_dropped(self, view: str) -> None: self.filtered_reports.append(view) -@platform_name("PowerBIReportServer") +@platform_name("PowerBI Report Server") @config_class(PowerBiReportServerDashboardSourceConfig) @support_status(SupportStatus.UNKNOWN) @capability(SourceCapability.OWNERSHIP, "Enabled by default") From c3939dc35f507bea81eaf5b792644a1b1a326990 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Wed, 10 Aug 2022 14:14:03 +0300 Subject: [PATCH 26/47] fix: fix docs --- .../docs/sources/powerbi-report-server/powerbi-report-server.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server.md b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server.md index 33133b39e5437..5be69ec9a711a 100644 --- a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server.md +++ b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server.md @@ -1,6 +1,6 @@ ## Configuration Notes See the -[Microsoft AD App Creation doc](https://docs.microsoft.com/en-us/sql/reporting-services/security/grant-user-access-to-a-report-server?view=sql-server-ver16) +[Microsoft Grant user access to a Report Server doc](https://docs.microsoft.com/en-us/sql/reporting-services/security/grant-user-access-to-a-report-server?view=sql-server-ver16) ## Concept mapping From f5a865757b6b73cd240055396bed22847cd1f7a6 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Wed, 10 Aug 2022 14:25:42 +0300 Subject: [PATCH 27/47] fix: fix docs in code --- .../powerbi-report-server.md | 4 +- .../powerbi_report_server/report_server.py | 47 +++++++++++-------- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server.md b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server.md index 5be69ec9a711a..ca600f1078675 100644 --- a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server.md +++ b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server.md @@ -1,7 +1,7 @@ ## Configuration Notes See the -[Microsoft Grant user access to a Report Server doc](https://docs.microsoft.com/en-us/sql/reporting-services/security/grant-user-access-to-a-report-server?view=sql-server-ver16) - +1. [Microsoft Grant user access to a Report Server doc](https://docs.microsoft.com/en-us/sql/reporting-services/security/grant-user-access-to-a-report-server?view=sql-server-ver16) +2. Use your user credentials from previous step in yaml file ## Concept mapping | Power BI Report Server | Datahub | diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py index 78a5795856adb..07db61c1ca462 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py @@ -1,6 +1,6 @@ ######################################################### # -# Meta Data Ingestion From the Power BI Report Server +# Meta Data Ingestion From the PowerBI Report Server # ######################################################### import logging @@ -101,7 +101,7 @@ class PowerBiReportServerDashboardSourceConfig(PowerBiReportServerAPIConfig): class PowerBiReportServerAPI: - # API endpoints of PowerBi Report Server to fetch reports, datasets + # API endpoints of PowerBI Report Server to fetch reports, datasets def __init__(self, config: PowerBiReportServerAPIConfig) -> None: self.__config: PowerBiReportServerAPIConfig = config @@ -130,7 +130,7 @@ def requests_get(self, url_http: str, url_https: str, content_type: str): ) # Check if we got response from PowerBi Report Server if response.status_code != 200: - message: str = "Failed to fetch Report from power-bi-report-server for" + message: str = "Failed to fetch Report from powerbi-report-server for" LOGGER.warning(message) LOGGER.warning("{}={}".format(Constant.ReportId, content_type)) raise ValueError(message) @@ -139,7 +139,7 @@ def requests_get(self, url_http: str, url_https: str, content_type: str): def get_all_reports(self) -> List[Any]: """ - Fetch all Reports from PowerBiReportServer + Fetch all Reports from PowerBI Report Server """ report_types_mapping: Dict[str, Any] = { Constant.REPORTS: Report, @@ -243,7 +243,7 @@ def get_owner_by_name(self, user_name: str) -> Optional[CorpUser]: class Mapper: """ - Transfrom PowerBi Report Server concept Report to DataHub concept Dashboard + Transfrom PowerBI Report Server concept Report to DataHub concept Dashboard """ class EquableMetadataWorkUnit(MetadataWorkUnit): @@ -311,7 +311,7 @@ def __to_datahub_dashboard( user_mcps: List[MetadataChangeProposalWrapper], ) -> List[MetadataChangeProposalWrapper]: """ - Map PowerBi Report Server report to Datahub Dashboard + Map PowerBI Report Server report to Datahub Dashboard """ dashboard_urn = builder.make_dashboard_urn( self.__config.platform_name, report.get_urn_part() @@ -415,7 +415,7 @@ def custom_properties( def to_datahub_user(self, user: CorpUser) -> List[MetadataChangeProposalWrapper]: """ - Map PowerBi Report Server user to datahub user + Map PowerBI Report Server user to datahub user """ LOGGER.info("Converting user {} to datahub's user".format(user.username)) @@ -501,20 +501,29 @@ class PowerBiReportServerDashboardSource(Source): """ This plugin extracts the following: - - Power BI Report Server Dashboards - - Names, descriptions and URLs of Dashboard - - Owners of Dashboards + Metadata that can be ingested: + - report name + - report description + - ownership(can add existing users in DataHub as owners) + - transfer folders structure to DataHub as it is in Report Server + - webUrl to report in Report Server + + Due to limits of PBIRS REST API, it's impossible to ingest next data for now: + - tiles info + - datasource of report + - dataset of report + + Next types of report can be ingested: + - PowerBI report(.pbix) + - Paginated report(.rdl) + - Mobile report + - Linked report ## Configuration Notes See the - 1. [Microsoft AD App Creation doc] - (https://docs.microsoft.com/en-us/power-bi/developer/embedded/embed-service-principal) - for the steps to create a app client ID and secret. - 2. Login to Power BI as Admin and from `Tenant settings` allow below permissions. - - Allow service principles to use Power BI APIs - - Allow service principals to use read-only Power BI admin APIs - - Enhance admin APIs responses with detailed metadata + 1. [Microsoft Grant user access to a Report Server doc](https://docs.microsoft.com/en-us/sql/reporting-services/security/grant-user-access-to-a-report-server?view=sql-server-ver16) + 2. Use your user credentials from previous step in yaml file """ source_config: PowerBiReportServerDashboardSourceConfig @@ -541,9 +550,9 @@ def get_workunits(self) -> Iterable[MetadataWorkUnit]: """ Datahub Ingestion framework invoke this method """ - LOGGER.info("PowerBiReportServer plugin execution is started") + LOGGER.info("PowerBI Report Server plugin execution is started") - # Fetch PowerBiReportServer reports for given url + # Fetch PowerBI Report Server reports for given url reports = self.powerbi_client.get_all_reports() for report in reports: From f8bb760c09fe84e4ed4c9cf614b6b1054493ad78 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Wed, 10 Aug 2022 14:27:38 +0300 Subject: [PATCH 28/47] fix: run linters --- .../ingestion/source/powerbi_report_server/__init__.py | 4 ++-- .../ingestion/source/powerbi_report_server/report_server.py | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py index a44a97fd60d0f..dfdf1301a6d3c 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py @@ -1,3 +1,5 @@ +from datahub.ingestion.source.powerbi_report_server.constants import Constant +from datahub.ingestion.source.powerbi_report_server.graphql_domain import CorpUser from datahub.ingestion.source.powerbi_report_server.report_server import ( PowerBiReportServerDashboardSource, ) @@ -5,5 +7,3 @@ PowerBiReport, Report, ) -from datahub.ingestion.source.powerbi_report_server.graphql_domain import CorpUser -from datahub.ingestion.source.powerbi_report_server.constants import Constant \ No newline at end of file diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py index 07db61c1ca462..6d19d0f64117e 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py @@ -8,8 +8,8 @@ from dataclasses import field as dataclass_field from typing import Any, Dict, Iterable, List, Optional, Set -import requests import pydantic +import requests from orderedset import OrderedSet from requests.exceptions import ConnectionError from requests_ntlm import HttpNtlmAuth @@ -53,7 +53,9 @@ class PowerBiReportServerAPIConfig(EnvBasedSourceConfigBase): username: str = pydantic.Field(description="Windows account username") password: str = pydantic.Field(description="Windows account password") - workstation_name: str = pydantic.Field(default="localhost", description="Workstation name") + workstation_name: str = pydantic.Field( + default="localhost", description="Workstation name" + ) host_port: str = pydantic.Field(description="Power BI Report Server host URL") server_alias: str = pydantic.Field( default="", description="Alias for Power BI Report Server host URL" From be3d34012277ebfda1b3de08d915df5e114574c1 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Wed, 10 Aug 2022 14:35:26 +0300 Subject: [PATCH 29/47] fix: remove duplicate doc --- .../ingestion/source/powerbi_report_server/report_server.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py index 6d19d0f64117e..5c51374393278 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py @@ -520,12 +520,6 @@ class PowerBiReportServerDashboardSource(Source): - Paginated report(.rdl) - Mobile report - Linked report - - ## Configuration Notes - - See the - 1. [Microsoft Grant user access to a Report Server doc](https://docs.microsoft.com/en-us/sql/reporting-services/security/grant-user-access-to-a-report-server?view=sql-server-ver16) - 2. Use your user credentials from previous step in yaml file """ source_config: PowerBiReportServerDashboardSourceConfig From 368dddfdb7f31b03937fb2ad9bfb4d4a78a6d35f Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Mon, 29 Aug 2022 16:22:54 +0300 Subject: [PATCH 30/47] fix: fix tests --- .../source/powerbi_report_server/graphql_domain.py | 2 +- .../ingestion/source/powerbi_report_server/report_server.py | 6 +++--- .../powerbi_report_server/test_powerbi_report_server.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/graphql_domain.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/graphql_domain.py index a43b6c9446c16..0f1347651c372 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/graphql_domain.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/graphql_domain.py @@ -3,7 +3,7 @@ from pydantic import BaseModel, Field -from .constants import RelationshipDirection +from datahub.ingestion.source.powerbi_report_server.constants import RelationshipDirection class CorpUserEditableInfo(BaseModel): diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py index 5c51374393278..2691811e77815 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py @@ -43,9 +43,9 @@ StatusClass, ) -from .constants import API_ENDPOINTS, Constant -from .graphql_domain import CorpUser -from .report_server_domain import LinkedReport, MobileReport, PowerBiReport, Report +from datahub.ingestion.source.powerbi_report_server.constants import API_ENDPOINTS, Constant +from datahub.ingestion.source.powerbi_report_server.graphql_domain import CorpUser +from datahub.ingestion.source.powerbi_report_server.report_server_domain import LinkedReport, MobileReport, PowerBiReport, Report LOGGER = logging.getLogger(__name__) diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py index cd63588f62399..7e9d8d7a0ae02 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py @@ -13,7 +13,7 @@ def mock_user(*args, **kwargs): user_data = dict( - urn=f"urn:li:corpuser:User1", + urn="urn:li:corpuser:User1", type=Constant.CORP_USER, username="User1", properties=dict(active=True, displayName="User1", email="User1@foo.com"), From f6f185a8319765ca42b8fc44454637dc0a11bbbf Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Wed, 31 Aug 2022 20:05:23 +0300 Subject: [PATCH 31/47] fix: change ownership, add ownership type, include to config file --- .../powerbi-report-server_recipe.yml | 5 +- metadata-ingestion/setup.py | 2 +- .../source/powerbi_report_server/__init__.py | 2 +- .../powerbi_report_server/graphql_domain.py | 89 ------- .../powerbi_report_server/report_server.py | 237 +++++++++--------- .../report_server_domain.py | 106 ++++++++ 6 files changed, 224 insertions(+), 217 deletions(-) delete mode 100644 metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/graphql_domain.py diff --git a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server_recipe.yml b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server_recipe.yml index 72bfce7be1028..cf58da2b6a2e5 100644 --- a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server_recipe.yml +++ b/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server_recipe.yml @@ -19,7 +19,10 @@ source: report_virtual_directory_name: Reports # Your Power BI Report Server base virtual directory name for report server report_server_virtual_directory_name: ReportServer - + # Enable/Disable extracting ownership information of Dashboard + extract_ownership: True + # Set ownership type + ownership_type: TECHNICAL_OWNER sink: diff --git a/metadata-ingestion/setup.py b/metadata-ingestion/setup.py index 3a179e484fdd2..7cb62846eabac 100644 --- a/metadata-ingestion/setup.py +++ b/metadata-ingestion/setup.py @@ -309,7 +309,7 @@ def get_long_description(): "nifi": {"requests", "packaging"}, "powerbi": microsoft_common, "vertica": sql_common | {"sqlalchemy-vertica[vertica-python]==0.0.5"}, - "powerbi-report-server": {"orderedset"} | powerbi_report_server, + "powerbi-report-server": powerbi_report_server, } diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py index dfdf1301a6d3c..812ea86d450a0 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py @@ -1,9 +1,9 @@ from datahub.ingestion.source.powerbi_report_server.constants import Constant -from datahub.ingestion.source.powerbi_report_server.graphql_domain import CorpUser from datahub.ingestion.source.powerbi_report_server.report_server import ( PowerBiReportServerDashboardSource, ) from datahub.ingestion.source.powerbi_report_server.report_server_domain import ( PowerBiReport, Report, + CorpUser, ) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/graphql_domain.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/graphql_domain.py deleted file mode 100644 index 0f1347651c372..0000000000000 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/graphql_domain.py +++ /dev/null @@ -1,89 +0,0 @@ -from datetime import datetime -from typing import List, Optional - -from pydantic import BaseModel, Field - -from datahub.ingestion.source.powerbi_report_server.constants import RelationshipDirection - - -class CorpUserEditableInfo(BaseModel): - display_name: str = Field(alias="displayName") - title: str - about_me: Optional[str] = Field(alias="aboutMe") - teams: Optional[List[str]] - skills: Optional[List[str]] - picture_link: Optional[str] = Field(alias="pictureLink") - - -class CorpUserEditableProperties(CorpUserEditableInfo): - slack: Optional[str] - phone: Optional[str] - email: str - - -class CorpUserStatus(BaseModel): - active: bool - - -class GlobalTags(BaseModel): - tags: List[str] - - -class EntityRelationship(BaseModel): - type: str - direction: RelationshipDirection - entity: str - created: datetime - - -class EntityRelationshipsResult(BaseModel): - start: int - count: int - total: int - relationships: Optional[EntityRelationship] - - -class CorpUserProperties(BaseModel): - active: bool - display_name: str = Field(alias="displayName") - email: str - title: Optional[str] - manager: Optional["CorpUser"] - department_id: Optional[int] = Field(alias="departmentId") - department_name: Optional[str] = Field(alias="departmentName") - first_name: Optional[str] = Field(alias="firstName") - last_name: Optional[str] = Field(alias="lastName") - full_name: Optional[str] = Field(alias="fullName") - country_code: Optional[str] = Field(alias="countryCode") - - -class CorpUser(BaseModel): - urn: str - type: str - username: str - properties: CorpUserProperties - editable_properties: Optional[CorpUserEditableProperties] = Field( - alias="editableProperties" - ) - status: Optional[CorpUserStatus] - tags: Optional[GlobalTags] - relationships: Optional[EntityRelationshipsResult] - editableInfo: Optional[CorpUserEditableInfo] = Field(alias="editableInfo") - global_tags: Optional[GlobalTags] = Field(alias="globalTags") - - def get_urn_part(self): - return "{}".format(self.username) - - def __members(self): - return (self.username,) - - def __eq__(self, instance): - return ( - isinstance(instance, CorpUser) and self.__members() == instance.__members() - ) - - def __hash__(self): - return hash(self.__members()) - - -CorpUserProperties.update_forward_refs() diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py index 2691811e77815..e98d7bb1a4867 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py @@ -10,7 +10,7 @@ import pydantic import requests -from orderedset import OrderedSet +from datahub.utilities.dedup_list import deduplicate_list from requests.exceptions import ConnectionError from requests_ntlm import HttpNtlmAuth @@ -44,8 +44,15 @@ ) from datahub.ingestion.source.powerbi_report_server.constants import API_ENDPOINTS, Constant -from datahub.ingestion.source.powerbi_report_server.graphql_domain import CorpUser -from datahub.ingestion.source.powerbi_report_server.report_server_domain import LinkedReport, MobileReport, PowerBiReport, Report +from datahub.ingestion.source.powerbi_report_server.report_server_domain import ( + LinkedReport, + MobileReport, + PowerBiReport, + Report, + CorpUser, + Owner, + OwnershipData +) LOGGER = logging.getLogger(__name__) @@ -67,9 +74,12 @@ class PowerBiReportServerAPIConfig(EnvBasedSourceConfigBase): report_server_virtual_directory_name: str = pydantic.Field( description="Report Server Virtual Directory URL name" ) - scan_timeout: int = pydantic.Field( - default=60, - description="time in seconds to wait for Power BI metadata scan result.", + extract_ownership: bool = pydantic.Field( + default=True, description="Whether ownership should be ingested" + ) + ownership_type: str = pydantic.Field( + default=OwnershipTypeClass.NONE, + description="Ownership type of owner", ) @property @@ -174,75 +184,6 @@ def get_all_reports(self) -> List[Any]: return reports -class UserDao: - def __init__(self, config: PowerBiReportServerDashboardSourceConfig): - self.__config = config - - def _run_query(self, query) -> Dict[str, Any]: - request = requests.post(url=self.__config.graphql_url, json={"query": query}) - if request.status_code == 200: - return request.json() - else: - raise Exception( - "Query failed to run by returning code of {}. {}".format( - request.status_code, query - ) - ) - - def _get_owner_by_name(self, user_name: str) -> Dict[str, Any]: - get_owner = f"""{{ - search(input: {{ type: CORP_USER, query: "{user_name}"}}){{ - searchResults{{ - entity{{ - urn - type - ...on CorpUser {{ - username - urn - type - properties {{ - active - displayName - email - title - }} - }} - }} - matchedFields {{ - name - value - }} - }} - }} - }} - """ - - result = self._run_query(get_owner) - return result - - @staticmethod - def _filter_values(response: Dict[str, Any], mask: str) -> Optional[Dict[str, Any]]: - users = response["data"]["search"]["searchResults"] - for user in users: - if user["matchedFields"][0]["value"] == mask: - return user["entity"] - return None - - def get_owner_by_name(self, user_name: str) -> Optional[CorpUser]: - response = self._get_owner_by_name(user_name=user_name) - filtered_response = self._filter_values(response=response, mask=user_name) - if filtered_response: - return CorpUser.parse_obj(filtered_response) - - user_data = dict( - urn=f"urn:li:corpuser:{user_name}", - type=Constant.CORP_USER, - username=user_name, - properties=dict(active=True, displayName=user_name, email=""), - ) - return CorpUser.parse_obj(user_data) - - class Mapper: """ Transfrom PowerBI Report Server concept Report to DataHub concept Dashboard @@ -296,16 +237,29 @@ def __to_work_unit( @staticmethod def to_urn_set(mcps: List[MetadataChangeProposalWrapper]) -> List[str]: - return list( - OrderedSet( + return deduplicate_list( [ mcp.entityUrn for mcp in mcps if mcp is not None and mcp.entityUrn is not None ] - ) ) + def to_ownership_set( + self, + mcps: List[MetadataChangeProposalWrapper], + existing_owners: List[OwnerClass], + ) -> List[Owner]: + ownership = [ + Owner(owner=owner.owner, type=owner.type) for owner in existing_owners + ] + for mcp in mcps: + if mcp is not None and mcp.entityUrn is not None: + ownership.append( + Owner(owner=mcp.entityUrn, type=self.__config.ownership_type) + ) + return deduplicate_list(ownership) + def __to_datahub_dashboard( self, report: Report, @@ -320,7 +274,9 @@ def __to_datahub_dashboard( ) chart_urn_list: List[str] = self.to_urn_set(chart_mcps) - user_urn_list: List[str] = self.to_urn_set(user_mcps) + user_urn_list: List[Owner] = self.to_ownership_set( + mcps=user_mcps, existing_owners=report.user_info.existing_owners + ) def custom_properties( _report: Report, @@ -328,6 +284,10 @@ def custom_properties( return { "workspaceName": "PowerBI Report Server", "workspaceId": self.__config.host_port, + "createdBy": report.created_by, + "createdDate": str(report.created_date), + "modifiedBy": report.modified_by or "", + "modifiedDate": str(report.modified_date) or str(report.created_date), "dataSource": str( [report.connection_string for report in _report.data_sources] ) @@ -376,9 +336,7 @@ def custom_properties( # Dashboard Ownership owners = [ - OwnerClass(owner=user_urn, type=OwnershipTypeClass.BUSINESS_OWNER) - for user_urn in user_urn_list - if user_urn is not None + OwnerClass(owner=user.owner, type=user.type) for user in user_urn_list ] ownership = OwnershipClass(owners=owners) # Dashboard owner MCP @@ -419,50 +377,55 @@ def to_datahub_user(self, user: CorpUser) -> List[MetadataChangeProposalWrapper] """ Map PowerBI Report Server user to datahub user """ - LOGGER.info("Converting user {} to datahub's user".format(user.username)) - - # Create an URN for User - user_urn = builder.make_user_urn(user.get_urn_part()) - - user_info_instance = CorpUserInfoClass( - displayName=user.properties.display_name, - email=user.properties.email, - title=user.properties.title, - active=True, - ) - - info_mcp = self.new_mcp( - entity_type=Constant.CORP_USER, - entity_urn=user_urn, - aspect_name=Constant.CORP_USER_INFO, - aspect=user_info_instance, - ) - - # removed status mcp - status_mcp = self.new_mcp( - entity_type=Constant.CORP_USER, - entity_urn=user_urn, - aspect_name=Constant.STATUS, - aspect=StatusClass(removed=False), - ) - - user_key = CorpUserKeyClass(username=user.username) + user_mcps = [] + if user: + LOGGER.info("Converting user {} to datahub's user".format(user.username)) + + # Create an URN for User + user_urn = builder.make_user_urn(user.get_urn_part()) + + user_info_instance = CorpUserInfoClass( + displayName=user.properties.display_name, + email=user.properties.email, + title=user.properties.title, + active=True, + ) - user_key_mcp = self.new_mcp( - entity_type=Constant.CORP_USER, - entity_urn=user_urn, - aspect_name=Constant.CORP_USER_KEY, - aspect=user_key, - ) + info_mcp = self.new_mcp( + entity_type=Constant.CORP_USER, + entity_urn=user_urn, + aspect_name=Constant.CORP_USER_INFO, + aspect=user_info_instance, + ) + user_mcps.append(info_mcp) + + # removed status mcp + status_mcp = self.new_mcp( + entity_type=Constant.CORP_USER, + entity_urn=user_urn, + aspect_name=Constant.STATUS, + aspect=StatusClass(removed=False), + ) + user_mcps.append(status_mcp) + user_key = CorpUserKeyClass(username=user.username) + + user_key_mcp = self.new_mcp( + entity_type=Constant.CORP_USER, + entity_urn=user_urn, + aspect_name=Constant.CORP_USER_KEY, + aspect=user_key, + ) + user_mcps.append(user_key_mcp) - return [info_mcp, status_mcp, user_key_mcp] + return user_mcps - def to_datahub_work_units(self, report: Report) -> Set[EquableMetadataWorkUnit]: + def to_datahub_work_units(self, report: Report) -> List[EquableMetadataWorkUnit]: mcps = [] - + user_mcps = [] LOGGER.info("Converting Dashboard={} to DataHub Dashboard".format(report.name)) # Convert user to CorpUser - user_mcps = self.to_datahub_user(report.user_info) + if user_info := report.user_info.owner_to_add: + user_mcps = self.to_datahub_user(user_info) # Convert tiles to charts ds_mcps: List[Any] chart_mcps: List[Any] @@ -480,7 +443,7 @@ def to_datahub_work_units(self, report: Report) -> Set[EquableMetadataWorkUnit]: # Convert MCP to work_units work_units = map(self.__to_work_unit, mcps) # Return set of work_unit - return OrderedSet([wu for wu in work_units if wu is not None]) + return deduplicate_list([wu for wu in work_units if wu is not None]) @dataclass @@ -535,7 +498,6 @@ def __init__( self.auth = PowerBiReportServerAPI(self.source_config).get_auth_credentials self.powerbi_client = PowerBiReportServerAPI(self.source_config) self.mapper = Mapper(config) - self.user_dao = UserDao(config) @classmethod def create(cls, config_dict, ctx): @@ -553,9 +515,7 @@ def get_workunits(self) -> Iterable[MetadataWorkUnit]: for report in reports: try: - report.user_info = self.user_dao.get_owner_by_name( - user_name=report.display_name - ) + report.user_info = self.get_user_info(report) except pydantic.ValidationError as e: message = "Error ({}) occurred while loading User {}(id={})".format( e, report.name, report.id @@ -574,6 +534,33 @@ def get_workunits(self) -> Iterable[MetadataWorkUnit]: # Return workunit to Datahub Ingestion framework yield workunit + def get_user_info(self, report: Any) -> OwnershipData: + existing_ownership: List[OwnerClass] = [] + dashboard_urn = builder.make_dashboard_urn( + self.source_config.platform_name, report.get_urn_part() + ) + user_urn = builder.make_user_urn(report.display_name) + + if ownership := self.ctx.graph.get_ownership(entity_urn=dashboard_urn): + existing_ownership = ownership.owners + if owner_to_check := self.ctx.graph.get_aspect_v2( + entity_urn=user_urn, aspect="corpUserInfo", aspect_type=CorpUserInfoClass + ): + existing_ownership.append( + OwnerClass(owner=user_urn, type=self.source_config.ownership_type) + ) + return OwnershipData(existing_owners=existing_ownership) + user_data = dict( + urn=user_urn, + type=Constant.CORP_USER, + username=report.display_name, + properties=dict(active=True, displayName=report.display_name, email=""), + ) + owner_to_add = CorpUser(**user_data) + return OwnershipData( + existing_owners=existing_ownership, owner_to_add=owner_to_add + ) + def get_report(self) -> SourceReport: return self.report diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py index d36b968d0846e..70befb5f6f620 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py @@ -3,6 +3,9 @@ from pydantic import BaseModel, Field, validator +from datahub.metadata.schema_classes import OwnerClass +from datahub.ingestion.source.powerbi_report_server.constants import RelationshipDirection + class CatalogItem(BaseModel): id: str = Field(alias="Id") @@ -257,4 +260,107 @@ class System(BaseModel): time_zone: str = Field(alias="TimeZone") +class Owner(BaseModel): + owner: str + type: str + + def __members(self): + return self.owner, self.type + + def __eq__(self, instance): + return isinstance(instance, Owner) and self.__members() == instance.__members() + + def __hash__(self): + return hash(self.__members()) + + +class CorpUserEditableInfo(BaseModel): + display_name: str = Field(alias="displayName") + title: str + about_me: Optional[str] = Field(alias="aboutMe") + teams: Optional[List[str]] + skills: Optional[List[str]] + picture_link: Optional[str] = Field(alias="pictureLink") + + +class CorpUserEditableProperties(CorpUserEditableInfo): + slack: Optional[str] + phone: Optional[str] + email: str + + +class CorpUserStatus(BaseModel): + active: bool + + +class GlobalTags(BaseModel): + tags: List[str] + + +class EntityRelationship(BaseModel): + type: str + direction: RelationshipDirection + entity: str + created: datetime + + +class EntityRelationshipsResult(BaseModel): + start: int + count: int + total: int + relationships: Optional[EntityRelationship] + + +class CorpUserProperties(BaseModel): + active: bool + display_name: str = Field(alias="displayName") + email: str + title: Optional[str] + manager: Optional["CorpUser"] + department_id: Optional[int] = Field(alias="departmentId") + department_name: Optional[str] = Field(alias="departmentName") + first_name: Optional[str] = Field(alias="firstName") + last_name: Optional[str] = Field(alias="lastName") + full_name: Optional[str] = Field(alias="fullName") + country_code: Optional[str] = Field(alias="countryCode") + + +class CorpUser(BaseModel): + urn: str + type: str + username: str + properties: CorpUserProperties + editable_properties: Optional[CorpUserEditableProperties] = Field( + alias="editableProperties" + ) + status: Optional[CorpUserStatus] + tags: Optional[GlobalTags] + relationships: Optional[EntityRelationshipsResult] + editableInfo: Optional[CorpUserEditableInfo] = Field(alias="editableInfo") + global_tags: Optional[GlobalTags] = Field(alias="globalTags") + + def get_urn_part(self): + return "{}".format(self.username) + + def __members(self): + return (self.username,) + + def __eq__(self, instance): + return ( + isinstance(instance, CorpUser) and self.__members() == instance.__members() + ) + + def __hash__(self): + return hash(self.__members()) + + +class OwnershipData(BaseModel): + existing_owners: Optional[List[OwnerClass]] = [] + owner_to_add: Optional[CorpUser] + + class Config: + arbitrary_types_allowed = True + + CatalogItem.update_forward_refs() +CorpUserProperties.update_forward_refs() From 2697f698d246c86896bc142f294e71debef8dde7 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Fri, 2 Sep 2022 11:57:23 +0300 Subject: [PATCH 32/47] fix: userdao --- .../powerbi_report_server/test_powerbi_report_server.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py index 7e9d8d7a0ae02..d953500194da6 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py @@ -8,8 +8,6 @@ from tests.test_helpers import mce_helpers FROZEN_TIME = "2022-02-03 07:00:00" -POWERBI_REPORT_SERVER_USER_DAO_GET_OWNER = "datahub.ingestion.source.powerbi_report_server.report_server.UserDao.get_owner_by_name" - def mock_user(*args, **kwargs): user_data = dict( From 22788ae96bb8ab43be7bf02b97932c3a42a3b597 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Fri, 2 Sep 2022 11:58:23 +0300 Subject: [PATCH 33/47] fix: fix test test_powerbi_report_server.py --- .../powerbi_report_server/test_powerbi_report_server.py | 1 - 1 file changed, 1 deletion(-) diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py index d953500194da6..405beddff2d4f 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py @@ -163,7 +163,6 @@ def default_source_config(): @freeze_time(FROZEN_TIME) -@mock.patch(POWERBI_REPORT_SERVER_USER_DAO_GET_OWNER, side_effect=mock_user) @mock.patch("requests_ntlm.HttpNtlmAuth") def test_powerbi_ingest( mock_msal, user_mock, pytestconfig, tmp_path, mock_time, requests_mock From 3eb0fb5389d0bd44e055f6f993a4e027b513c3d4 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Fri, 2 Sep 2022 11:59:21 +0300 Subject: [PATCH 34/47] fix: fix test test_powerbi_report_server.py --- .../powerbi_report_server/test_powerbi_report_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py index 405beddff2d4f..1f13fa55587dd 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py @@ -165,7 +165,7 @@ def default_source_config(): @freeze_time(FROZEN_TIME) @mock.patch("requests_ntlm.HttpNtlmAuth") def test_powerbi_ingest( - mock_msal, user_mock, pytestconfig, tmp_path, mock_time, requests_mock + mock_msal, pytestconfig, tmp_path, mock_time, requests_mock ): test_resources_dir = pytestconfig.rootpath / "tests/integration/powerbi" From a6b3a6d46b679279ba6131d1627129577c9c03f2 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Fri, 2 Sep 2022 11:59:44 +0300 Subject: [PATCH 35/47] fix: fix test test_powerbi_report_server.py --- .../powerbi_report_server/test_powerbi_report_server.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py index 1f13fa55587dd..553a697009f80 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py @@ -9,15 +9,6 @@ FROZEN_TIME = "2022-02-03 07:00:00" -def mock_user(*args, **kwargs): - user_data = dict( - urn="urn:li:corpuser:User1", - type=Constant.CORP_USER, - username="User1", - properties=dict(active=True, displayName="User1", email="User1@foo.com"), - ) - return CorpUser.parse_obj(user_data) - def register_mock_api(request_mock): api_vs_response = { From 92c0010c9600b29cb77fec0a088923e864e6192f Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Fri, 2 Sep 2022 12:09:57 +0300 Subject: [PATCH 36/47] fix: fix test test_powerbi_report_server.py --- .../source/powerbi_report_server/report_server.py | 2 +- .../test_powerbi_report_server.py | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py index e98d7bb1a4867..1f019d3382751 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py @@ -543,7 +543,7 @@ def get_user_info(self, report: Any) -> OwnershipData: if ownership := self.ctx.graph.get_ownership(entity_urn=dashboard_urn): existing_ownership = ownership.owners - if owner_to_check := self.ctx.graph.get_aspect_v2( + if self.ctx.graph.get_aspect_v2( entity_urn=user_urn, aspect="corpUserInfo", aspect_type=CorpUserInfoClass ): existing_ownership.append( diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py index 553a697009f80..545efa16daac2 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py @@ -1,6 +1,7 @@ from datetime import datetime from unittest import mock +from datahub.metadata.schema_classes import OwnerClass from freezegun import freeze_time from datahub.ingestion.run.pipeline import Pipeline @@ -10,6 +11,14 @@ FROZEN_TIME = "2022-02-03 07:00:00" +def mock_existing_users(*args, **kwargs): + return [OwnerClass(owner="urn:li:corpuser:TEST_USER", type="TECHNICAL_OWNER"), ] + + +def mock_user_to_add(*args, **kwargs): + return None + + def register_mock_api(request_mock): api_vs_response = { "https://host_port/Reports/api/v2.0/Reports": { @@ -154,9 +163,11 @@ def default_source_config(): @freeze_time(FROZEN_TIME) +@mock.patch("PipelineContext.graph.get_ownership", side_effect=mock_existing_users) +@mock.patch("PipelineContext.graph.get_aspect_v2", side_effect=mock_user_to_add) @mock.patch("requests_ntlm.HttpNtlmAuth") def test_powerbi_ingest( - mock_msal, pytestconfig, tmp_path, mock_time, requests_mock + mock_msal, mock_existing_users, mock_user_to_add, pytestconfig, tmp_path, mock_time, requests_mock ): test_resources_dir = pytestconfig.rootpath / "tests/integration/powerbi" From 00bfa8ab28e720a5b2eced1674237a3ee755caae Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Fri, 2 Sep 2022 12:20:07 +0300 Subject: [PATCH 37/47] fix: fix test test_powerbi_report_server.py --- .../powerbi_report_server/test_powerbi_report_server.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py index 545efa16daac2..58899f95e7478 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py @@ -163,11 +163,9 @@ def default_source_config(): @freeze_time(FROZEN_TIME) -@mock.patch("PipelineContext.graph.get_ownership", side_effect=mock_existing_users) -@mock.patch("PipelineContext.graph.get_aspect_v2", side_effect=mock_user_to_add) @mock.patch("requests_ntlm.HttpNtlmAuth") def test_powerbi_ingest( - mock_msal, mock_existing_users, mock_user_to_add, pytestconfig, tmp_path, mock_time, requests_mock + mock_msal, pytestconfig, tmp_path, mock_time, requests_mock ): test_resources_dir = pytestconfig.rootpath / "tests/integration/powerbi" @@ -190,6 +188,11 @@ def test_powerbi_ingest( }, } ) + pipeline.ctx.graph.get_ownership = mock.MagicMock() + pipeline.ctx.graph.get_ownership.side_effect = mock_existing_users + pipeline.ctx.graph.get_aspect_v2 = mock.MagicMock() + pipeline.ctx.graph.get_aspect_v2.side_effect = mock_user_to_add + pipeline.run() pipeline.raise_from_status() From 189785a171e6635c6e3bd791d4e08f9af0254eaa Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Fri, 2 Sep 2022 12:21:21 +0300 Subject: [PATCH 38/47] fix: fix test test_powerbi_report_server.py --- .../powerbi_report_server/test_powerbi_report_server.py | 1 + 1 file changed, 1 insertion(+) diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py index 58899f95e7478..bac9c9cc8999b 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py @@ -188,6 +188,7 @@ def test_powerbi_ingest( }, } ) + pipeline.ctx.graph = mock.MagicMock() pipeline.ctx.graph.get_ownership = mock.MagicMock() pipeline.ctx.graph.get_ownership.side_effect = mock_existing_users pipeline.ctx.graph.get_aspect_v2 = mock.MagicMock() From 6e6a6136a709de5fb6a2f70db5e7209ba927b1d8 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Fri, 2 Sep 2022 12:27:10 +0300 Subject: [PATCH 39/47] fix: fix test test_powerbi_report_server.py --- .../test_powerbi_report_server.py | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py index bac9c9cc8999b..1c39b672f36ab 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py @@ -1,18 +1,31 @@ from datetime import datetime from unittest import mock -from datahub.metadata.schema_classes import OwnerClass from freezegun import freeze_time from datahub.ingestion.run.pipeline import Pipeline from datahub.ingestion.source.powerbi_report_server import Constant, CorpUser +from datahub.metadata.schema_classes import AuditStampClass, OwnerClass, OwnershipClass from tests.test_helpers import mce_helpers FROZEN_TIME = "2022-02-03 07:00:00" def mock_existing_users(*args, **kwargs): - return [OwnerClass(owner="urn:li:corpuser:TEST_USER", type="TECHNICAL_OWNER"), ] + return OwnershipClass( + owners=[ + OwnerClass( + { + "owner": "urn:li:corpuser:TEST_USER", + "type": "TECHNICAL_OWNER", + "source": None, + } + ) + ], + lastModified=AuditStampClass( + {"time": 0, "actor": "urn:li:corpuser:unknown", "impersonator": None} + ), + ) def mock_user_to_add(*args, **kwargs): @@ -164,9 +177,7 @@ def default_source_config(): @freeze_time(FROZEN_TIME) @mock.patch("requests_ntlm.HttpNtlmAuth") -def test_powerbi_ingest( - mock_msal, pytestconfig, tmp_path, mock_time, requests_mock -): +def test_powerbi_ingest(mock_msal, pytestconfig, tmp_path, mock_time, requests_mock): test_resources_dir = pytestconfig.rootpath / "tests/integration/powerbi" register_mock_api(request_mock=requests_mock) @@ -194,7 +205,6 @@ def test_powerbi_ingest( pipeline.ctx.graph.get_aspect_v2 = mock.MagicMock() pipeline.ctx.graph.get_aspect_v2.side_effect = mock_user_to_add - pipeline.run() pipeline.raise_from_status() mce_out_file = "../powerbi/golden_test_ingest.json" From dc1333b11a081085fa8958d9aa4e9422b67733d7 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Fri, 2 Sep 2022 12:29:02 +0300 Subject: [PATCH 40/47] fix: fix test test_powerbi_report_server.py --- .../powerbi_report_server/test_powerbi_report_server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py index 1c39b672f36ab..8e6e26de2fee0 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py @@ -15,7 +15,7 @@ def mock_existing_users(*args, **kwargs): return OwnershipClass( owners=[ OwnerClass( - { + **{ "owner": "urn:li:corpuser:TEST_USER", "type": "TECHNICAL_OWNER", "source": None, @@ -23,7 +23,7 @@ def mock_existing_users(*args, **kwargs): ) ], lastModified=AuditStampClass( - {"time": 0, "actor": "urn:li:corpuser:unknown", "impersonator": None} + **{"time": 0, "actor": "urn:li:corpuser:unknown", "impersonator": None} ), ) From 69447c307a08dff004d30f5f283b594c438b65a1 Mon Sep 17 00:00:00 2001 From: RChygir <30907647+RChygir@users.noreply.github.com> Date: Fri, 2 Sep 2022 12:30:49 +0300 Subject: [PATCH 41/47] fix: update golden file --- .../powerbi/golden_test_ingest.json | 406 ++++++++++++------ 1 file changed, 275 insertions(+), 131 deletions(-) diff --git a/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json b/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json index 62ec08afd2f9a..bb2ac045a130f 100644 --- a/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json +++ b/metadata-ingestion/tests/integration/powerbi/golden_test_ingest.json @@ -1,28 +1,77 @@ [ { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)", - "entityKeyAspect": null, + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:TEST_USER", + "changeType": "UPSERT", + "aspectName": "corpUserInfo", + "aspect": { + "value": "{\"active\": true, \"displayName\": \"TEST_USER\", \"email\": \"\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test" + } +}, +{ + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:TEST_USER", + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test" + } +}, +{ + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:TEST_USER", "changeType": "UPSERT", - "aspectName": "datasetProperties", + "aspectName": "corpUserKey", + "aspect": { + "value": "{\"username\": \"TEST_USER\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test" + } +}, +{ + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938a)", + "changeType": "UPSERT", + "aspectName": "browsePaths", + "aspect": { + "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Testa\"]}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test" + } +}, +{ + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938a)", + "changeType": "UPSERT", + "aspectName": "dashboardInfo", "aspect": { - "value": "{\"customProperties\": {}, \"description\": \"issue_history\", \"tags\": []}", + "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"createdBy\": \"TEST_USER\", \"createdDate\": \"2022-02-03 07:00:00\", \"modifiedBy\": \"TEST_USER\", \"modifiedDate\": \"2022-02-03 07:00:00\", \"dataSource\": \"\"}, \"title\": \"Testa\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Testa\"}", "contentType": "application/json" }, "systemMetadata": { "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null + "runId": "powerbi-report-server-test" } }, { - "auditHeader": null, - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)", - "entityKeyAspect": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938a)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -31,36 +80,54 @@ }, "systemMetadata": { "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null + "runId": "powerbi-report-server-test" + } +}, +{ + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938a)", + "changeType": "UPSERT", + "aspectName": "dashboardKey", + "aspect": { + "value": "{\"dashboardTool\": \"powerbi-report-server\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938a\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test" + } +}, +{ + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938a)", + "changeType": "UPSERT", + "aspectName": "ownership", + "aspect": { + "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:TEST_USER\", \"type\": \"TECHNICAL_OWNER\"}, {\"owner\": \"urn:li:corpuser:TEST_USER\", \"type\": \"NONE\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test" } }, { - "auditHeader": null, "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User1@foo.com", - "entityKeyAspect": null, + "entityUrn": "urn:li:corpuser:TEST_USER", "changeType": "UPSERT", "aspectName": "corpUserInfo", "aspect": { - "value": "{\"active\": true, \"displayName\": \"user1\", \"email\": \"User1@foo.com\", \"title\": \"user1\"}", + "value": "{\"active\": true, \"displayName\": \"TEST_USER\", \"email\": \"\"}", "contentType": "application/json" }, "systemMetadata": { "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null + "runId": "powerbi-report-server-test" } }, { - "auditHeader": null, "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User1@foo.com", - "entityKeyAspect": null, + "entityUrn": "urn:li:corpuser:TEST_USER", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -69,55 +136,110 @@ }, "systemMetadata": { "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null + "runId": "powerbi-report-server-test" } }, { - "auditHeader": null, "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User1@foo.com", - "entityKeyAspect": null, + "entityUrn": "urn:li:corpuser:TEST_USER", "changeType": "UPSERT", "aspectName": "corpUserKey", "aspect": { - "value": "{\"username\": \"User1@foo.com\"}", + "value": "{\"username\": \"TEST_USER\"}", "contentType": "application/json" }, "systemMetadata": { "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null + "runId": "powerbi-report-server-test" + } +}, +{ + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "changeType": "UPSERT", + "aspectName": "browsePaths", + "aspect": { + "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Testb\"]}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test" + } +}, +{ + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "changeType": "UPSERT", + "aspectName": "dashboardInfo", + "aspect": { + "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"createdBy\": \"TEST_USER\", \"createdDate\": \"2022-02-03 07:00:00\", \"modifiedBy\": \"TEST_USER\", \"modifiedDate\": \"2022-02-03 07:00:00\", \"dataSource\": \"\"}, \"title\": \"Testb\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Testb\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test" + } +}, +{ + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test" + } +}, +{ + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "changeType": "UPSERT", + "aspectName": "dashboardKey", + "aspect": { + "value": "{\"dashboardTool\": \"powerbi-report-server\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938b\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test" + } +}, +{ + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938b)", + "changeType": "UPSERT", + "aspectName": "ownership", + "aspect": { + "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:TEST_USER\", \"type\": \"TECHNICAL_OWNER\"}, {\"owner\": \"urn:li:corpuser:TEST_USER\", \"type\": \"NONE\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test" } }, { - "auditHeader": null, "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User2@foo.com", - "entityKeyAspect": null, + "entityUrn": "urn:li:corpuser:TEST_USER", "changeType": "UPSERT", "aspectName": "corpUserInfo", "aspect": { - "value": "{\"active\": true, \"displayName\": \"user2\", \"email\": \"User2@foo.com\", \"title\": \"user2\"}", + "value": "{\"active\": true, \"displayName\": \"TEST_USER\", \"email\": \"\"}", "contentType": "application/json" }, "systemMetadata": { "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null + "runId": "powerbi-report-server-test" } }, { - "auditHeader": null, "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User2@foo.com", - "entityKeyAspect": null, + "entityUrn": "urn:li:corpuser:TEST_USER", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -126,55 +248,54 @@ }, "systemMetadata": { "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null + "runId": "powerbi-report-server-test" } }, { - "auditHeader": null, "entityType": "corpuser", - "entityUrn": "urn:li:corpuser:users.User2@foo.com", - "entityKeyAspect": null, + "entityUrn": "urn:li:corpuser:TEST_USER", "changeType": "UPSERT", "aspectName": "corpUserKey", "aspect": { - "value": "{\"username\": \"User2@foo.com\"}", + "value": "{\"username\": \"TEST_USER\"}", "contentType": "application/json" }, "systemMetadata": { "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null + "runId": "powerbi-report-server-test" } }, { - "auditHeader": null, - "entityType": "chart", - "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", - "entityKeyAspect": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938c)", "changeType": "UPSERT", - "aspectName": "chartInfo", + "aspectName": "browsePaths", "aspect": { - "value": "{\"customProperties\": {\"datasetId\": \"05169CD2-E713-41E6-9600-1D8066D95445\", \"reportId\": \"\", \"datasetWebUrl\": \"http://localhost/groups/64ED5CAD-7C10-4684-8180-826122881108/datasets/05169CD2-E713-41E6-9600-1D8066D95445/details\", \"createdFrom\": \"Dataset\"}, \"title\": \"test_tile\", \"description\": \"test_tile\", \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"inputs\": [{\"string\": \"urn:li:dataset:(urn:li:dataPlatform:postgres,library_db.public.issue_history,DEV)\"}]}", + "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Testc\"]}", "contentType": "application/json" }, "systemMetadata": { "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null + "runId": "powerbi-report-server-test" } }, { - "auditHeader": null, - "entityType": "chart", - "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", - "entityKeyAspect": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938c)", + "changeType": "UPSERT", + "aspectName": "dashboardInfo", + "aspect": { + "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"createdBy\": \"TEST_USER\", \"createdDate\": \"2022-02-03 07:00:00\", \"modifiedBy\": \"TEST_USER\", \"modifiedDate\": \"2022-02-03 07:00:00\", \"dataSource\": \"\"}, \"title\": \"Testc\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Testc\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test" + } +}, +{ + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938c)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -183,74 +304,110 @@ }, "systemMetadata": { "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null + "runId": "powerbi-report-server-test" } }, { - "auditHeader": null, - "entityType": "chart", - "entityUrn": "urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)", - "entityKeyAspect": null, + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938c)", "changeType": "UPSERT", - "aspectName": "chartKey", + "aspectName": "dashboardKey", + "aspect": { + "value": "{\"dashboardTool\": \"powerbi-report-server\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938c\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test" + } +}, +{ + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938c)", + "changeType": "UPSERT", + "aspectName": "ownership", + "aspect": { + "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:TEST_USER\", \"type\": \"TECHNICAL_OWNER\"}, {\"owner\": \"urn:li:corpuser:TEST_USER\", \"type\": \"NONE\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test" + } +}, +{ + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:TEST_USER", + "changeType": "UPSERT", + "aspectName": "corpUserInfo", + "aspect": { + "value": "{\"active\": true, \"displayName\": \"TEST_USER\", \"email\": \"\"}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test" + } +}, +{ + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:TEST_USER", + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": false}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1643871600000, + "runId": "powerbi-report-server-test" + } +}, +{ + "entityType": "corpuser", + "entityUrn": "urn:li:corpuser:TEST_USER", + "changeType": "UPSERT", + "aspectName": "corpUserKey", "aspect": { - "value": "{\"dashboardTool\": \"powerbi\", \"chartId\": \"powerbi.linkedin.com/charts/B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0\"}", + "value": "{\"username\": \"TEST_USER\"}", "contentType": "application/json" }, "systemMetadata": { "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null + "runId": "powerbi-report-server-test" } }, { - "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938d)", "changeType": "UPSERT", "aspectName": "browsePaths", "aspect": { - "value": "{\"paths\": [\"/powerbi/64ED5CAD-7C10-4684-8180-826122881108\"]}", + "value": "{\"paths\": [\"/powerbi_report_server/dev/server_alias/Reports/path/to/Testd\"]}", "contentType": "application/json" }, "systemMetadata": { "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null + "runId": "powerbi-report-server-test" } }, { - "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938d)", "changeType": "UPSERT", "aspectName": "dashboardInfo", "aspect": { - "value": "{\"customProperties\": {\"chartCount\": \"1\", \"workspaceName\": \"demo-workspace\", \"workspaceId\": \"7D668CAD-7FFC-4505-9215-655BCA5BEBAE\"}, \"title\": \"test_dashboard\", \"description\": \"test_dashboard\", \"charts\": [\"urn:li:chart:(powerbi,charts.B8E293DC-0C83-4AA0-9BB9-0A8738DF24A0)\"], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"https://localhost/dashboards/web/1\"}", + "value": "{\"customProperties\": {\"workspaceName\": \"PowerBI Report Server\", \"workspaceId\": \"host_port\", \"createdBy\": \"TEST_USER\", \"createdDate\": \"2022-02-03 07:00:00\", \"modifiedBy\": \"TEST_USER\", \"modifiedDate\": \"2022-02-03 07:00:00\", \"dataSource\": \"\"}, \"title\": \"Testd\", \"description\": \"\", \"charts\": [], \"datasets\": [], \"lastModified\": {\"created\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}, \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}, \"dashboardUrl\": \"http://host_port/Reports/powerbi/path/to/Testd\"}", "contentType": "application/json" }, "systemMetadata": { "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null + "runId": "powerbi-report-server-test" } }, { - "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938d)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -259,48 +416,35 @@ }, "systemMetadata": { "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null + "runId": "powerbi-report-server-test" } }, { - "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938d)", "changeType": "UPSERT", "aspectName": "dashboardKey", "aspect": { - "value": "{\"dashboardTool\": \"powerbi\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/7D668CAD-7FFC-4505-9215-655BCA5BEBAE\"}", + "value": "{\"dashboardTool\": \"powerbi-report-server\", \"dashboardId\": \"powerbi.linkedin.com/dashboards/ee56dc21-248a-4138-a446-ee5ab1fc938d\"}", "contentType": "application/json" }, "systemMetadata": { "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null + "runId": "powerbi-report-server-test" } }, { - "auditHeader": null, "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(powerbi,dashboards.7D668CAD-7FFC-4505-9215-655BCA5BEBAE)", - "entityKeyAspect": null, + "entityUrn": "urn:li:dashboard:(powerbi-report-server,reports.ee56dc21-248a-4138-a446-ee5ab1fc938d)", "changeType": "UPSERT", "aspectName": "ownership", "aspect": { - "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:users.User1@foo.com\", \"type\": \"NONE\"}, {\"owner\": \"urn:li:corpuser:users.User2@foo.com\", \"type\": \"NONE\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", + "value": "{\"owners\": [{\"owner\": \"urn:li:corpuser:TEST_USER\", \"type\": \"TECHNICAL_OWNER\"}, {\"owner\": \"urn:li:corpuser:TEST_USER\", \"type\": \"NONE\"}], \"lastModified\": {\"time\": 0, \"actor\": \"urn:li:corpuser:unknown\"}}", "contentType": "application/json" }, "systemMetadata": { "lastObserved": 1643871600000, - "runId": "powerbi-test", - "registryName": null, - "registryVersion": null, - "properties": null + "runId": "powerbi-report-server-test" } } ] \ No newline at end of file From de8988b4fb05e73f2a21dd5fc201e67f28981daa Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Fri, 9 Sep 2022 13:59:44 +0300 Subject: [PATCH 42/47] fix: fix report_server.py ownership --- .../powerbi_report_server/report_server.py | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py index 1f019d3382751..a082267bc257a 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py @@ -10,7 +10,6 @@ import pydantic import requests -from datahub.utilities.dedup_list import deduplicate_list from requests.exceptions import ConnectionError from requests_ntlm import HttpNtlmAuth @@ -29,6 +28,19 @@ ) from datahub.ingestion.api.source import Source, SourceReport from datahub.ingestion.api.workunit import MetadataWorkUnit +from datahub.ingestion.source.powerbi_report_server.constants import ( + API_ENDPOINTS, + Constant, +) +from datahub.ingestion.source.powerbi_report_server.report_server_domain import ( + CorpUser, + LinkedReport, + MobileReport, + Owner, + OwnershipData, + PowerBiReport, + Report, +) from datahub.metadata.com.linkedin.pegasus2avro.common import ChangeAuditStamps from datahub.metadata.schema_classes import ( BrowsePathsClass, @@ -42,17 +54,7 @@ OwnershipTypeClass, StatusClass, ) - -from datahub.ingestion.source.powerbi_report_server.constants import API_ENDPOINTS, Constant -from datahub.ingestion.source.powerbi_report_server.report_server_domain import ( - LinkedReport, - MobileReport, - PowerBiReport, - Report, - CorpUser, - Owner, - OwnershipData -) +from datahub.utilities.dedup_list import deduplicate_list LOGGER = logging.getLogger(__name__) @@ -238,11 +240,11 @@ def __to_work_unit( @staticmethod def to_urn_set(mcps: List[MetadataChangeProposalWrapper]) -> List[str]: return deduplicate_list( - [ - mcp.entityUrn - for mcp in mcps - if mcp is not None and mcp.entityUrn is not None - ] + [ + mcp.entityUrn + for mcp in mcps + if mcp is not None and mcp.entityUrn is not None + ] ) def to_ownership_set( @@ -536,6 +538,8 @@ def get_workunits(self) -> Iterable[MetadataWorkUnit]: def get_user_info(self, report: Any) -> OwnershipData: existing_ownership: List[OwnerClass] = [] + if not self.source_config.extract_ownership: + return OwnershipData(existing_owners=[], owner_to_add=None) dashboard_urn = builder.make_dashboard_urn( self.source_config.platform_name, report.get_urn_part() ) @@ -544,7 +548,7 @@ def get_user_info(self, report: Any) -> OwnershipData: if ownership := self.ctx.graph.get_ownership(entity_urn=dashboard_urn): existing_ownership = ownership.owners if self.ctx.graph.get_aspect_v2( - entity_urn=user_urn, aspect="corpUserInfo", aspect_type=CorpUserInfoClass + entity_urn=user_urn, aspect="corpUserInfo", aspect_type=CorpUserInfoClass ): existing_ownership.append( OwnerClass(owner=user_urn, type=self.source_config.ownership_type) From 0ccef3346231a233d5c579e264278f648716deb6 Mon Sep 17 00:00:00 2001 From: "oleks.lytvynov" Date: Sat, 10 Sep 2022 08:47:03 +0300 Subject: [PATCH 43/47] fix: linters fix --- .../ingestion/source/powerbi_report_server/__init__.py | 2 +- .../ingestion/source/powerbi_report_server/report_server.py | 3 +-- .../source/powerbi_report_server/report_server_domain.py | 4 +++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py index 812ea86d450a0..45db1725ca1d1 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/__init__.py @@ -3,7 +3,7 @@ PowerBiReportServerDashboardSource, ) from datahub.ingestion.source.powerbi_report_server.report_server_domain import ( + CorpUser, PowerBiReport, Report, - CorpUser, ) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py index a082267bc257a..ff8b6d55e0631 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py @@ -4,8 +4,7 @@ # ######################################################### import logging -from dataclasses import dataclass -from dataclasses import field as dataclass_field +from dataclasses import dataclass, field as dataclass_field from typing import Any, Dict, Iterable, List, Optional, Set import pydantic diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py index 70befb5f6f620..839540141822e 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py @@ -3,8 +3,10 @@ from pydantic import BaseModel, Field, validator +from datahub.ingestion.source.powerbi_report_server.constants import ( + RelationshipDirection, +) from datahub.metadata.schema_classes import OwnerClass -from datahub.ingestion.source.powerbi_report_server.constants import RelationshipDirection class CatalogItem(BaseModel): From 8d56c8b6ff2d06df230e6a6d3855c3569df9f58d Mon Sep 17 00:00:00 2001 From: Shirshanka Das Date: Fri, 28 Oct 2022 16:48:13 -0700 Subject: [PATCH 44/47] fix lint --- .../source/powerbi_report_server/report_server.py | 5 +++-- .../source/powerbi_report_server/report_server_domain.py | 4 ++-- .../powerbi_report_server/test_powerbi_report_server.py | 9 ++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py index ff8b6d55e0631..31ebabcad61c6 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py @@ -5,7 +5,7 @@ ######################################################### import logging from dataclasses import dataclass, field as dataclass_field -from typing import Any, Dict, Iterable, List, Optional, Set +from typing import Any, Dict, Iterable, List import pydantic import requests @@ -127,7 +127,7 @@ def __init__(self, config: PowerBiReportServerAPIConfig) -> None: def get_auth_credentials(self): return self.__auth - def requests_get(self, url_http: str, url_https: str, content_type: str): + def requests_get(self, url_http: str, url_https: str, content_type: str) -> Any: try: LOGGER.info("Request to Report URL={}".format(url_https)) response = requests.get( @@ -544,6 +544,7 @@ def get_user_info(self, report: Any) -> OwnershipData: ) user_urn = builder.make_user_urn(report.display_name) + assert self.ctx.graph if ownership := self.ctx.graph.get_ownership(entity_urn=dashboard_urn): existing_ownership = ownership.owners if self.ctx.graph.get_aspect_v2( diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py index 839540141822e..adcbcaaed96e6 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server_domain.py @@ -41,12 +41,12 @@ def validate_diplay_name(cls, value, values): # noqa: N805 def get_urn_part(self): return "reports.{}".format(self.id) - def get_web_url(self, base_reports_url: str): + def get_web_url(self, base_reports_url: str) -> str: return "{}powerbi{}".format(base_reports_url, self.path) def get_browse_path( self, base_folder: str, workspace: str, env: str, report_directory: str - ): + ) -> str: return "/{}/{}/{}/{}{}".format( base_folder, env.lower(), workspace, report_directory, self.path ) diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py index 8e6e26de2fee0..f4b90dc5db29c 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py @@ -4,7 +4,6 @@ from freezegun import freeze_time from datahub.ingestion.run.pipeline import Pipeline -from datahub.ingestion.source.powerbi_report_server import Constant, CorpUser from datahub.metadata.schema_classes import AuditStampClass, OwnerClass, OwnershipClass from tests.test_helpers import mce_helpers @@ -14,16 +13,16 @@ def mock_existing_users(*args, **kwargs): return OwnershipClass( owners=[ - OwnerClass( - **{ + OwnerClass.from_obj( + { "owner": "urn:li:corpuser:TEST_USER", "type": "TECHNICAL_OWNER", "source": None, } ) ], - lastModified=AuditStampClass( - **{"time": 0, "actor": "urn:li:corpuser:unknown", "impersonator": None} + lastModified=AuditStampClass.from_obj( + {"time": 0, "actor": "urn:li:corpuser:unknown", "impersonator": None} ), ) From 905e3834994a86a46dfcc91cde67c217024f8cfc Mon Sep 17 00:00:00 2001 From: Shirshanka Das Date: Sat, 29 Oct 2022 08:09:08 -0700 Subject: [PATCH 45/47] remove walrus operator for 3.7 compatibility --- .../ingestion/source/powerbi_report_server/report_server.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py index 31ebabcad61c6..3bd4424af9d92 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py @@ -425,7 +425,8 @@ def to_datahub_work_units(self, report: Report) -> List[EquableMetadataWorkUnit] user_mcps = [] LOGGER.info("Converting Dashboard={} to DataHub Dashboard".format(report.name)) # Convert user to CorpUser - if user_info := report.user_info.owner_to_add: + user_info = report.user_info.owner_to_add + if user_info: user_mcps = self.to_datahub_user(user_info) # Convert tiles to charts ds_mcps: List[Any] @@ -545,7 +546,8 @@ def get_user_info(self, report: Any) -> OwnershipData: user_urn = builder.make_user_urn(report.display_name) assert self.ctx.graph - if ownership := self.ctx.graph.get_ownership(entity_urn=dashboard_urn): + ownership = self.ctx.graph.get_ownership(entity_urn=dashboard_urn) + if ownership: existing_ownership = ownership.owners if self.ctx.graph.get_aspect_v2( entity_urn=user_urn, aspect="corpUserInfo", aspect_type=CorpUserInfoClass From e9f798855087ea9d3a1c2c34f2a1c30a1643e25f Mon Sep 17 00:00:00 2001 From: Shirshanka Das Date: Mon, 31 Oct 2022 22:01:59 -0700 Subject: [PATCH 46/47] fix goldens, change platform to powerbi --- .../src/images/powerbireportserverlogo.png | Bin 163473 -> 0 bytes .../powerbi_report_server/report_server.py | 2 +- .../powerbi/golden_test_ingest.json | 406 ++++++----------- .../golden_test_ingest.json | 410 +++++++++++++++--- .../test_powerbi_report_server.py | 4 +- .../main/resources/boot/data_platforms.json | 12 +- 6 files changed, 488 insertions(+), 346 deletions(-) delete mode 100644 datahub-web-react/src/images/powerbireportserverlogo.png diff --git a/datahub-web-react/src/images/powerbireportserverlogo.png b/datahub-web-react/src/images/powerbireportserverlogo.png deleted file mode 100644 index 03519c2e3b0e3c46e1d9e379c2b0982cab99f6b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 163473 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%0ABY z<7fXjuFg%wYlxwuCOMnli8VX=Q6AkPmze`0w!M<7%8ZPO{m2UppL4N#x39{U{@i@_ zfejIB)P4+8p;ty72iq9)BRCc$O8Hpm{-r1| zvaU;v1TA^AT@T4gSdP^TL?9Zwvew{p3SK=RDMuc|>q&RVGLmOj4ysQ|_;?a9i3TEs zfdlVMXU#PtVΜjh=B5tKUv5X(a4m)7Fm5tTrPR1;Vuo`US(uN1N%0yFp)O9ff?n zHWPP~={HZB_n&{f9dCeAv#=Vmku(e5{`sUN^b%h^(mB*9CsSkYas z_aCQ^gO#MyOE2BHHCkD%U{@@A;d}V7%uQ>!Z+(CaJDGVhab$TX>hE*=h1W&uAlYJa zW_NC%y5a3tQaB-4%EIxbW9W-vIY%923pFoL9#i&tCbIH%!cr^uDV}q-==?m0v5Ym8 z1`?dVCM#tc{>h)!&%9(=?D>)SYgnI#eTPj*chmrA#!<|Nxn)T)#%1Ghdswb@gSO8B z_cnVH(~6(uL7D!VXH8tD5KqbY=Jm1bJ2ii2eR4hA#4cV>+6q6xD)#Kat7h;jl%`f^ zWjl1FjJkqBh$owk=A!CDWLU>UR~7Ax*c9{STMM|_PTAk4O)GF?v32rxPYpLD=9>Xz z5M_8gHKqtz8KCdl_D96vnn0d=>wQ|rzMnxVcf!|0wTMUsPPZ1>lKLO_PB#ZR{DN-N zNB_1^{IvR1V_0tGjaGQkx7CAExW$fBhiFp@hcN)V3)3-W1sWfkQ@YMAUfh1LcDcaH zH)%iT>z`jwk6e!kKU#Ud3znk)eq!;;TEA6>q&5kb2hAa;+`5(pr+ziTx+niFY4(cO%Rru>Yx$HqtGKoep=oI&uNg{X z{~BnS4bu?CDs0=0n>$NVyeT`i_I%nA=)Z+?3`d)L$zO+Mn#LR!-{7_K`hp&_Tgi;P zs6J$Q0I6!lElkgN$gi*z;ng1P8?UqP0m8Cdbk!jBMfOMp9=#+|;GVe)L~;**17ycI z-NG%x_Y9BTkc<1xDRbwwq4`wIoi1iaa6Gc=ez^QSl3K^lE_A%XzEby8YM0%{S@kOd zTPdF?3q-sC3w|}RI<_o*_=UDQF=XQ1#NwU>P&U>3eGo19#R)6A$kS!Q?4@7DfpPxC zzqI;IISo@H}XV{PKl9X#SCrLZwTU*fQEWs~m_o8of66uVchWn|AmaE%n0TYWZ=#r@vB?>hM2O z5o5pXsrFRg$KrK3c=h#h*<2|GnF{Ac&zOQSo)b1|l*bW~*6P}Yab1@*ODoN^$yb*J zyE&BR);9NG2s#UIvbo$e6HJ@9u9S>FPrLxSHl)^kSSYin*pRS4t~nV`Hl_dW=y;;&{m>6RCwax z*ATnS*8j=ZvqdXIP43_C91lzdg-*(tqhzdX!_!g%7eR3jUK`OHH>(CV2r*_600oYV zlz+OO@+l7MUSHsF=FN7boq_W(7>l&9R4u^)`S_83p0D;;5ofx zwVz%R$+D)lby|4^2fPqY>HA+6-P?-%Z+`46=E8`!G)+btvkQRreT?S^(es1>XV^tM z-k-~XJaNG$`#06b8eZqJCQ?Zwi%4wM3xL;Y{ZgOO)PJ*@1Jw?;s+zg=!+A}QzA1o> zC>IW1zT*HYjR|aFY1T*y0Aj?HD`q$F+{G^Ma(a%6r41+#H;&n} zZ#;z<&*`X|&YhsPjuDm1r#a#cs$N#B1)Bemz$lM9OX14AwPctUI0C??r7G-!Xjd5F zD5DCId-UT!0U>_}gkgOeb$V7UBWxW9okKbee+z9tWz0@)*-zpZOYd9Zgf+oGm$hZw z6!zp2%UXyThL*#ur|6UeCva?S`BoEjXWf=sCQdTQ8I6`hvts&Rnh$>{?@kSm_Te$| zvB=Vb&Flse zV$giAcsluh)(m=N|pqh4bIo@OVsZx&q=Xr;(+9=9SLC-rZ?E5S6)E@ zvLnQc`*!-p2bRSH%((qP=f}@vK;gmotVTz-oh~xPF#1oSrnD?SlMTAwT{2X&Ihe^s zPW+_-|5+Htnt^BvZ@w&ao@Snsxe7wO)-bAZKojQ*?F>Aj#(tyYMr`g;ejPqAO7!=l+;S8;cpxx z4_$mG@>J{0;QnmlhwnOMku-S*Eoqkf^?1+EbkbE%;fSi`7cYGus$wZs9K`+5lm-7i zE9;iq0Q9xP116~6V)>zN_O#1Fypu=Rm?-z6@&Xh(Uf7$q&?AgcV3VuY$ZDFm*+~e( z+Zbg)rj9^2M4>kx?4r+U-v6PGU@LLn4qa$^Yu*%W!t{UyBS9+w`6hr@e~_#gM8`$K zr=A}GTHxWy$IT4z@}e=Tu`82pH^-KhO$hxi8gFD7i-x7WlP*`{fLqEI+HKHk%Qov& z)eEf_931VHz%>z#zLz6xdkq$VE)RE-Q!L&PjeHoR5N}lQoIDn7v0FG%zq41GN{DGA z9vdtq!7_n?fM&c2EV(i$2W#G~K|d;z&gWq_Dkljsx}J1Poz+J<6rQd<>U_Qbz(+u0 zZX5@9@fqUUPOeu#Zk_-^-YK#pNX5^j;|zF=#Y*~o-;SfEOw9~j;y?;h0)sa{MD!Y> zB>gS?ew{@^&u*Fc#c}z$IZ5+x(0Gi0Ij+*2a-tY}J}dj&PuY?ZKapZii$bnAP%)<+ zY|Mel;%r)3;DuJgSrbLdTIHZIEESjOVv0|{DLsyk6rG#$Fx93nT6$rCeqVN0Tp_Z+ zvBd^O5HGN<$e6dcP_u`n>xU&b4m@ZRE#^T-A+*e%1UD63gY%)k@egd}?Ut?I+E&ay zS@QbJB51ib@MJ7EDyFtV)#UprvwL{ z6d!@_1V?$YE>PhjhhEpj8a<>ecJJg_2%~`4jsH!1&s(vsEe*g)CYooRZ^->jE&zId z*=}i4!bDN`abC~{Jr%>&p4WLDeYGiW@`&aYx!c}fehPa{1{?tDto-VAEFV?|szIEDr9n%f&q^{m ziQ8CGgDJ7+uM5U=Rpq@|5*EQ?Z1^2S_I98mhhcuZ|=6XIB2r}sj=hM zLmdqty+y0=A*t^PQwPs;P}*|aig=iDT0R*rR;*!-?w@Uxl%^J_l_(mU93+&eMLhp< z>%!|!M2D$16Ij=$tA;Cs1B;pOI02Ugn{Z+^`)|YRZDp$5JWstIo3yr?PE9!K+ti$p z?voq%Y)*M+{q5Vo`ZsP1ZgNv}IO>g!k)QcH_`2gbPTyA(F{h(!iqO#Ftbce=aRPNXY29~-RLtLE~`x+i~YBpVw zCh!eg4n_M-7a`H4yeu$I;pgDA#(9B{61UB4{w0$xh|wLC>>dHuR(O9e6np z16xn-uxDlk74qaU1jL-Nj3XBNhCZ$-bCBoRp3-^r>dz^(l1{!li^$ZG>Q8IwIVzl) z)?cMY8z02P>aIZ=6@{8pa;O`K=Oxx|En5~QnrmI~8D2n6SgYMd8?8T@DB+SvFb*>5 z71ddkzzo0%?W-e;Y|$hlH1UcW<=(aYKtJMKUr1$>cM;aGKa)uqQbyDkn^Fs?sJjrU zB+`2nK|8*jVhOWBP5InU_Tn*trTHg{^uF(g|HwS10l8#v{1i|q3wc3XPKoPL1wa#k zJ3)%^U-8*RwZsT~*y~CN2h;(5 zM~`VJaX<%=dc(lG-d-4=6h*3#=7^}?GX|1bM}%}5U%4XJB5kEk_48xn&;LnheQ?BF}DXhjcxliJ;#L8b4OC0#-uw$ zvMX9gD{;k~7t|uIqeSw6)GY^ez1i714p{M>3$6LU;5tpoKCDC=A8|;!&SCkw1Gg|k ziIb>=O_pYdPgs|{zog$k9Wa`FSDp2uvA zBxJnqz9u2heZ@sT9+K2_Wx8*}r+vJkUu27L<}EiMwTC_)Z{i3A+27Wqi2$m6-+h1g zQpgru_?a0^W~Qvxo#`?}iu#rCVjx>fG~U?MGItHM%IL65H0WqSE%HCxqLM=HMdkN% ziMDKojzX=U{+DHSZg2aDxYw>CK|3fZxX&b14ASPSPI2(h7;aP9)xpAw0V?o5OQL~KzVV+-gs8(+_= z2dZI1A`;}o)vgGzg`@2g3@E4N7`@4?eT^FWXW75oY{>+S;wn;~blHtjKMi84CMMXR z`ISPNaUj2em9G=59#UH1uqmK_5Y>_T6-#d-))t;GJN)^W)e6|JmS8R}@OS?WmkhlA>`j@IA+ zOU&u!Lv~N~Hn}*-dkJHchIm~TXA3&cnuB&w{25hH3PiKAcm)XQR~y7zRj;^Z+C`Eu zmsHiv3fmAPon1AJ(SgvpoAuTSayfKEY{&zqbu6byffOu#foo;^idwuuL6u6d%G%yt zh%@>F;^HwpCc^nCl~P&Zf*!EZ4*57(My1Ld;~SnpS7F3%~7^j3|Q|rtRO?idmGJfx-5utlF%VC zPYhB&K19sA?Y*|kx&P=G<6G#=@yK$y@t%{fBJfNHTLQiq5}7m-R%7)%o(fsNE!sJ* z(YvW&yj4p*J|bKJQddOlyt*(3y33{$lf5GzAo@u|Bwn}k5No+Z5w zv=J@1dqod#i+;zF(3#uSg~^3jN_w6VDLiWX52XH)t|3W8<_FG(o$T}Hm&$$co4!_= z+%*A^rnYq+y|;}g+Bag2?#h(Z!!+HaIlhnE-<647m7WzOMV%^sFZAZ2j`sf8G^(n_ zZ7q__*W!J;w>H{35rd}bmSY^AqIvIVXY66w`mz!n6l24K5A4g+XtFtk zo85aD-CRNgCIzuZ-(F3ovG92rYnxjopcNODjaI}9^e4E0zR*qz_ps6&Z%aSj?evZ9 zzmg@|#A;P(yyQBDKMqrC&o8qi-R4OE!FTHM%bpbjF6Qr(IrBvbKcs!$E`q{<=K?7I zK3@Zv%A#B7GQejx(O%;+rN9cUgIKZTF^7UG`=9+y8u~g_B<_W^U@tH`7nfl6eFlS0-3vA< zxE>o@=EH;Hf2w!i8tBZnoNh=s;Mdarsv%m4H{tOC-0hAXe*6tCbPLG&g?%6QO^bEu zNQDG=WP?cBgUPWAzHR)hkd@qp+la`saRvgu&JnP*3ikKshZ#zlS8q2_S-`kgIe3-( z%C~?!(b46H6_}W`bTuEEb8^NGq2=(lkU6`F_m^ze97FFde=-Cc@SrcYZ?MMdzHyTM z;%@DAWf5r+FgE%RmDm2wORqRkx^Hm8(zIu?aeKkK1%AQLtPLWn=EUjd>?Ad83zn6y zV1Y3FPTLUoYPe38Fb+|@2#9a6SteTfBJ*ekb1XWwo0`*OHrhHCU1=@TRUG~fgjCML zkl!As3|GKqSWxyAQ(T6VJV^1ZuYnKSQOW&}l0f9p-E;a=;RdANC$OAKm2g;wDn$Qk zPdLhcUK`q#_mW`!mge#<95Sg4eP*XL$39eHw#1_cUKy#S8sdbE$&boF#a!Yn_zx6; zuY25y%Z%d+P0jJEJVy7KfkMicNcfr|AsI2b={G64FLNW{zeq(xYM3K<481mRYXPKd z4hN8mSw&J{vqa)ETtK>l+kub>4_^JfUiC8InF>6U9EBUKS0z;%_KevSa6KS33-lVs z-dGi3)21U47~!HTA%KwJnPAkLdIwjdHP=KfsgJ+h>-3T%{LrejcyR~DLrB$-B3KsP z#TJtL7dcOkln)2$$c+-=Lay1cO`PR~fkRR8=EIjolSQLN(MRnI*Zb$s^P^iA6L|D` zr1wtDbG5@?`#m*yYsrkOZurPb7sfIjHkV)S*Fe3=NbW?d&+KKU+P4-Yy+0T1KPH!1 z=;mX4RTY!v0toffuw8DR_D7du=MJDQ5FR^czM5*MiLy}y-^>kk$O|Ix1C96x#DUd8 zV}|L@SG0;kxPK3IRqR=x#Qcjs+E?{@2ZnT1eqAx^QCM2vGp+mp&+ds-jPxd#JMl-|VUs8X zjAYjYzXo3_m$nurSF(B2t9cCX@8O_4ZjY~ipL%zJniQ+9#Jrmce~2VBBr2q;q&ng2 zf>X+%v~c>$dj=YUPSU~pjEbNl34Md6ExF(sQh1qmUAkYx{?Y1cn7Xo@kNA1H%Mf1b z#J7OFz;j~H@5wo17;7NhlEqbGOlXd^Q>NC07!gzN>Dh6=f6ElC!?wCn>3mTWuUvB83Ype<_yJlpIO^;gn331yr=sL&k9sz*iFl>t&H zEIoVl0WhL=zLIvEgWMs*DOOU^U>(y>POVH2r76HA0$4*_#{{KU755oMwWv=X{gHV;7AImrc>{TE zgSHjUOChI3kt`Rqp^N&TJlF|!FAJe*3y)}hm9M}zV?Dk zZSJ^-?T8;fD6nBc*fXla^h*gjV1)ic9lzTN5PtX4=OyV9YLGabkqy7w>57wT*&V?}wErn3q_ws%{&{A&&Hhkn6vx!@A__ZoFkhZez8xC9amq-T!p$x^`?4~vRUu`cTuYKaaG&ddWn&Ra zB2V;Jy(0a$VB}K?lvK$Om~4Ik*Kf$&}a5uiq<^c!DBSU-Mq+Je;e;6 z3X%Qg{mnHK%I4T$TY2m1&A4UC%GBbad_Ck8-#RRFg^3?Op{C2&vZ=R5MX_?w8IZPu zi;i@Wgk>JXyKIXrhxH2HHl3g*%>;84#OmMfvX6oh-bq*7cx8i1ojbl!^~~(^hljo! ze24*Cd=_#h81!@;0@UF3*N;oyo6`u<@Lhq;#kGVH0UGf8TghKB5-jxeI z+xpNKK; z4Wp5H{>&a{v`JkyO0yuzShBwvzxq(d&Gw^d+6_r!!BJMsjE-*qPz2s!6@RCX1wx!5 z>=Vu=3pMxhiRH!gKa%8#04-pTgrf&!G_RbkEDtr_vsg8^pPe9sL{UbNlY(O-TbLBEI+5cyi}qcM;*X>LPWtZ2Fd9ia3M z=X0QZ&PkkB9j@L}GI%V}C}@w7m2Fa_x>6i{5Z$x2Uw|Dx=n_r#E!yQfe~gKN zL5&QtM2gG}&DS(su3Ca+_zT$=W}i+OO6hUFp4BDM|7-Ptmk0GNxgxc#z;cSM&-JS{ zRfCSS&K<^g7+Ub_T=C&9;k;+edQHz7mo!H#0UWVxKqo0^h`VqsF|1!)g1<%SmNZAf z)n;}5nUgsvlI@3a2;4X0pAq99LmFT(_i4}s_T04`6i{rgO22L*xlx}f(2g=m#!?l6 z%X#$g$1xx#5ELxM&aY9OSbCxascGdBz-%48VPIx;KcF|F#LPO*!N~Bo)k4&U48Vb% z4KhsQjrW7~u)8;TPSRD?gi0j76i2-p&8+U&&{u!J99$aOJktUIqFUq1b?%HY= zswv|q$lcOcqsTRm=Iq3Pq&w8{fE07oWlweWQoy+_7O&&8&qQ}WprsW&d?21x{UXI6 zbDnAqryb)`pF9GHD>uF@gxBgVsvFvxAUi8P${y6U6S~hupsmp;D;kZg#2J$N5Bd6^ zo7P?tjWR!qYp_Cp?Y>^vyS3?s&RCtEvzTQQmigs9NqT={uc^gMOL&iyv*!t75`y3@ zUL8y9Ca%;mr`k97YZ!*ye>X}ujU=2!N@dM{aW96Yndo*lN{8j7Jc&3|Dg7*6>r8JQ z(9fWzgfsir_xkuulo!YELP*4K2;mx|uEUCntwdf#CoD~0Ekjl`=wZ$C`?2zX!ZLL8 zir#riyw^;ONN?XN`9eq}ZcEU|7Tfj_5qj8M<18!Rs&tfXje0!#+eTMQ{Bo|KSV>vJ zz?l1_k1>EQgmxMM>mbzT`;XX(KN*X1=uFO7!wPe))vV;lV4CrQ0lcyCk zs;p6_5y}sU@RB{5Z@V-OWpD#B^MfRx&pxqi0^xrSP5sl*|Dmfq)*`|Rr|k}Lz-#5dHMM!W9pLa}*&JjEuoDG(;U1@# zj~B3f#t&ol{Qz8dH5-(XYf-hy$}302Vjy6B=@6L}CTF95LC6+ctbsBh8n8Sy^05;a z()97-K|97Knej;BN>dHG$l|n1QllsBp)1sWlF56EizRk7KC zH%~^35Fn<}D`_J~X<@vUl#Hm><}>=#)pj5}p)4=~6&@uz=Mq`fD|+#ZefJ_PSKCty zA{|bLmd+koBmcL}wy!m?jRWNbXcoBzUF4L+3o|h~ypE}wWo2p?@WWEvxtUrvWQte{ zCk8RVY@q5RDeBCWx#s0%euY>sZ#wS8riDmroqiNz>*h4JFi9Q zQ-GyvM}(K9uVOrIOu!15J1|T+5D_}G_?FAJle55ptR{uRe;Pb^W_?JWo zN4GmFd;SuD&g8DJ6Zf_jgVrB?y{C10);>p?4_)w9lXR~QR`g9R&<<8yG+teMn_=(p zK|)Ptuqr`aAi1sJxiePnid#bqgTvN^86@pnv{&qTMWr;I8IaJ<&%GTtB3wzHk|jCi z1w7BR^@$i}=0$D%>~Q?Y$@mAHjB+}#Ztk6lHRJ~#?qbITmO~RU+hEW9UEDR5AY~OdIrQr|JFBL8;I#TII4@sJoHlh*9*?|lhHOsN@DnAQJ?eW@4!PJ^sm+WXO ze#)D4weP~D%F(a)M)WjLZ@ZHFrCGw!t)!dISSEI{p~inG*dNwSB%}AJw=tU}u*P&g zweYL*=TP{vSYdEx{xA%dsegF7%Eyy6sd91wCtOg=2FowGpD<>>+KOoKfdK!Ki_hpf z_CO!tmMx$Jg26_~3Pzn9P47ovmdUrCPAzQ*1f!E;0dXk6fj}Nm{>nA_qN((Ap==QCTU^s6%eY z#r_=?$p7Yg{5EvZnk?rm<7JfMX{2FRwUher>wKRsW?YCCQHX}%D}jyC|D&Ze;VM0COVUDPsR}&mxcH?Z*Fn&-KJ=M84bc7z z)D8Qc@JDPRjpA@l(dnN3r0@R51XE3cOMIy7*b#0G$f}VEs?g0o7BC_9<~6&og;y=z z_C6w3=zs57w7lz8JTNVNHWmnC!94qmR`D5&lT`VPkdgUWDtqFM%9SpfSR^}97cCYZ z0?Q0XNgM@c$q(x6IT#xkuWJ&KN}1GxOUZ2n8S#C&vh%%PAwN2xBq<)0^lVy3Gqj+O z{WB`L)6(CrQj6Z@UaUBK~|j53x-&sv~<^6|=OW#(j#-jI3Z zXY0zwK}Ot*f&TP_EdJ%>6Iv}zxXfB9zA_NfhR#)|UI2_R|KQjTW8Zn=f(M#hj97iV zOf&_U$HD8hneL?@5V`V0=e(c406_`Q!aR4e=}z5O(5DcSc;1Nz+r4Zk5%n&o>NDq-x=e+gDzY!V>0XWjwBtnc8*NV&b0J zK8}|1<$yqmPELyvhi+=+Nv~WxBp|Lm;vj}DhZUp%yM5#Me|q*mhTuGCAk71mM_>Pw zj+vF2%swse)*yLlmv2PiLj;>Z?@ViKBdVLsDbxy*yGJbl@af)qFVLLJ@90xW!8eI~7< zBYOvpkr-fgVrT=I9qq?wB1WZ3 zYH4Z?Hm2M!ILg3lou0hDUtPoV$>kbFfK;;+V*ufudmM~8w{NI;k7UcQrmIp~b5UJW z02GyfUmUx1=EKah+l9M7A%)B^FTi*4Xt967Gn1$se#CyZi3%uRXM_BqmJeY6h zs&1Eq&Umxs-j5v%jE@Q?E(O$~DJxhxSv82vULVi1%u`S(Aqn;zT6BS>kLABOp~q*m zaZ9!^CON_EwN1}J;5jX<+!w$jmwPl84fTR$)MlS^)v0ufo>b5z+zd{W4{t~9)r7N) zDt`%x2dwa4x<)ppg1?m~0g`zZdBR6UJU_dvplSL9XjwHc6r`%_*1#HmXtnw3Y3ZLG zbAP#k?czYHsQrt`UpCSx#Cn*dfy_u5^^OCp(_6o#7bmsQ6Yg&mz7&}WXCHWK3~chQ zx+PD7#0sJL3)4Vt7^#$QoUS=`PkOr^PkZaSzlm73$^X=N?I}9rsyNr8x{0KC?zErr zvC&_PZCl}Dg-Ngyi`|5ChyN~8R#q(~wd&kV429ML$bfeVQr(RPw6UkLSt++p5Iezu z%ZltTv^C%u=Ix187Z5At0_MZO6k&SB67b!G055UX&0gQP!40wq&7BsT+kDA>g*Ha$ zJp6sR5vk~C9qlMF^a9?&(kDy?A!fNlYIT-~ShaxFAou@Wry z)L-(mDK<;UrVptp`D@dz%x!~e1Hpaa)glfZM#@vn39&{OueXvu6^(pRFeW8AnGLGx z7rG_mCWW63@Xy?()kLIO&*r3TSSA?lD*-sCa=9{1_Kuc}>nnsaRwmhVADzQo-~5@f zRT+mTThwX$iqzN${Ajhic#he7MEHM*yjV#}Ej13?+&9k$KvQ62AQL9iTbh(0<$(5kiuU;b$3cIz5`uijoT7}Oi~W)ef7tU! zpSAO-i02Q0-PMP^VR!9L5ZIo08lU1MJhyzLCT?0;T6X??n~#AJ~AAj%=K%`LOqs@m`J{XLQL$KMp#q;L864 zq@)uKxj5j{@BCV-qKb}rXNuJscpoMde;Qgf26!BfwZRIH<3o=a3rfwxx6Iw_@DV8M zi_!-peA$IF&2GTM4}J29MJ0>n^;MR~sZbwHCY1M`s|=akU$bvewuoAUUb>c2nLUDd zwSapykYU39>lb-!@M~R}dCoyjY<9xuplnYRTOex=e}MmYMBoqya)*P*+U6%TZ$XqD z#;6FpC3CxFbHbg}NhAMws!h*G{bpLHk*gi>I|>LLup(m2duvgyKy>#HQu=)}O24e#ThX>UX@f43mb3*Jc|bg>(b*(9 z<|xoxs`EQ!43_~bE(1ZYaq)-$8m(3rNF9W@gT~;QYsYD-nw0w4iEiqV8ynZ_+-!M4 zlx5-El2kY2LU96Dye=T%IAr`dhH3W*%P9*OFxTAz0d|SZP91e%R+cWHS)i!E=(hsa zf8sxU3lq-LUSrd-(Cy-wsr3*{gPz}!vk+DG(XI`TM)pzuB%;>vRQrXvz{&U=v0&+^%w-# zPAuLJT(s`kf?)<_B+*|(+;*ZW5R^7i{gpx~#>5_$L!F#d+EVfc%L5|6ch|4qJJY3l!Gb3UIxpvObJ@B6;4*Y$cmudY4U*_dmM3YMa5_5XD{ z0CD@YZL(ii^Oo;BDnLwMNO@ZML7WK8Pbb>hRAugQt)pnX@!2E{8SD9+gcj$rNn6fv*auwA_Dj|KgT+dwf4tus|6e3ES9Mw$&F$N#a9{s$_e532YWNN_?MEeYlif## z7jG4TW0AY54S|jnoB7jqVMfsUqQJ2$V_L^*1X}SQkqeaJK75h+W{nwlHV$umq+2e% zd?)ut^WG{4Xz4~215iwcZuB2iLM=77g%iFSlTLGiQe1IiC$OQVBZn_*GOKr;kv{(! z1ohY6pM8BFvH0i4IEXOgj?DwIDZ}}P-+}Vi5B)vz?gzf*5y#K5?NxD=zcgxX+`!9u zdx-UvYiV~WzIp#L^g`u7_iB|jiV*LUKYFmmk^|s@}*2Uyym2{QKT`_WHAGYQn7fvSl zNd+DIT}NA7fpKFs-AUZhq6u15$E%&#J;XiK{ZS*}W?<_`X0r$(+4PC8vQyCYkaTIcVlBR!vG@?B%lXNI$ zG8Ubbgk!c>FL`CH1l_0DzW@{7ezwp%_2FIZlq5^Cs_(=LP~E38*Umf*X25MU5KZfh z#bzc%ZXe<8a)mCjBmzPl5(!=V|L@QK51Y%G#qS?qm5K~=Yn`5V?EK}$#cr(%>$aU! zaua7GaM9}TtJM@3J;uuMMD>Ye2bZh#72I@>y5e1HrKaHP3!Nd{{p+~gB|FViw2(`E zqJ&IE*zZnX=>3JGD58IVed9KYEVN<4>e!xUI-X`>bE(KrkPh_}48=!Z>;%RYyCg82 z6}M+0va`j1$Y%fFpT8^NoJyf{@n?ILybMw#rj__YUB>XXp>pqsvr_5E7=D)106VRP`nf2hc7>^OUn zU`lIKVE-|W5JI$5Sk(bDoXt!H^SQb zbJt6*tp4Ex%G^@}5rpnH?)!DX^EZB>F1k6Z@kM0!;+rG`jJK2tK`%obe}vY<)_E&R zn8s=n@JG5=1Z((xz_T)c{LSdGGXh_b!8Y@^b&?`{rag^2f71k{3%QEylF}+`3Ob_pAb75|!%dII>%cM;PKaY-+(HTz+dHxkA{By*va%NsdWHg0)nmYu5 zJV0vPBTM$UityfW7PBu3`|NbbH)F=hKU|&SIivR*Z=ddnkurUTUk)fiSbyT3dTZRva@Wt9bh=yCh{fv>7}Z z7fnZ)1J&RSaI}M`1?ow5TS3ZbZl3qClNTe~MKJ>r*x+W1{*p1j81r<^ zp0wLIX?0U(EVuvlyu4R)L5BO<4t@@ODS>U z%}d3Z)U?q02DbJ?vk0#{V(rQR#5W3jV7~5&Nv}8Lg2gRCM7ly)U|Vdi&gU3TjZ=X7LrN zQ0P2R#!0%azgd~LY-?$~IJGxdOeN#7pK6WvFODD@_4wIZgBj5?1c%Gxw6eq=_n$&Z zkKu~{{3{px58=vuq@%JFPE?PiZ-}?3+x3V1HKkW_X|rC-!Fe+R1+Lq#U(zJ@b|{aG zJz9QPp-A5y4c;paa^{m~pVT-m1G9z2aX`W~p&Q%pye^?kkTz!swOp}k>OPBk5!^-D z2Kn2K(B|$KkbYKq|0(N6sKO-7;!|jeY$0{E8H=2Bv$A(;)^my03 z8GdXxK^agrg(!=QDn%6lCED$&(0mqx9fs2LCrry(&@Etwx` zct}Oo<2v`b4-UIB(+#mQ7M7$~0smTGZujf|Y3lQ{gwSzUjStT>R$tSf&}sD&bFq5= z^B;9$>zhuje^8iys=!VXT%K;J!=_5se~gpBt>BP28H<|v+9-x#7P8$C$q-m4Kh2}7 zeY=O^%K)>cxa*a2X3Twi3(IIsBvbg89Kb}3X#scxZ4V^#KuW!uDyo?B=4Q^&4kAs3 zsrKI&eg@>)ePQde~-s*tBlYY54L`l-sk6v$`a$Bf>o=kIocM zwhYL&^|rqEJZT^Fh?7HCWa7_l7H~3E4Qy#AS{NU=t|z!yU4w!TLs51TN?Idc5x#yW z;#@+u?8cmQq#Tun+1t7Ex8PwVnMR&IxrWWX{pu;6aKKM*#Jui@XXPJBoW+;vvRHJg ziDLHa*(qIX-O1tGCo3Lw-nV%l^vNmrpm#zOx0g#Jv!jD)f@%rOcu5CmH-FE_w0ly^ zK;|3Y&O2QDx^2eli{Ul{2R%ryg0BdD1dfud`Ff$22=fVr%$Si^j8pOEG$cfR<4+?w zt?v%7iBsxG6Fwy)f!T;-oQvGQkzr~*6U+E>8PoVf$#(sx`z*h{CI8%^4Z!3ppEBs^ zDu1vf;H30R~t)KTz2X&jtNa3;W=t9w$YmD zN#fw@zc!8$;YIcpV%7^4=+%X~E1~UcTO*@l}sM@Rj zMCVB>F_choH}6 zTbT%kWF2!5CB*2uljs7|hx*&l;QPBz9MJ-!+*A|H`j*WJR#K#I-MOWRKW>bnI|eox z?B>jWLaO_h4eq63a`0w%=>eXv+xC4?N9Uy>g=^0XT-yXs^z2V_vtbGBaD-|?(ok)@ zD6v(g@QefPuU_Y$dHZXFPO`Gq@~dYLKG`3zu2`MZo^i*YCfl`ieP(C}|JUP#@1?xO z(rf5l3+CLh6_;acmlxfhbKgw~k8pqeNPJ*~t9pPcFHMWHbl>6$`OrSu-TUl_+)YWB zvhr<=Nx9@miL>}qnDIIT8*(1mIWKyT1X5-|N9H$h3K$Y21Czf>L$WECDFfx%eMd$H z#{RUso~5RR`f^za0YXc(RN^TVWJBzJ#ZY11+xjDj@_$$K`q`FelG8o|iw#pBJc9pN zNIz)Tx4I~6`*?YNETeXL{!z%4g-Py8>fX$Z8P>iPM#nm%ue|2iI`uG9JqqXV**M5M93r_NKQdjszgm4leXCr0#JRd?>`R1Gv9-E|MD z=W^_BVq26A@~bbbjD=!gaJzWeKG>)5=t$eQJe~q$i;RUtf>}u)VsaGvBds2eVIf0j zt0Yf^B$EDh4KCX0?^^4esX}NFUk{n6T`XGn`S{RnCG*j>hjZ%}+kUSP zkzPsY(=>YZuKS&i)5mAYA^4BU-%PFt)7QO+Fk7CtUt zlgc>!!No18ju*-^UTfK$@orh%X#G0x(Y?wdN3VN>hptmD5UJ9C(x%h?gO`6Q?$Ni!m`{`fcK>_V) z1Cmp6d(OsS&co&|-Kag<_us!3?Y3ud;vl(~j2niq+3EzjiKp}HqDetmwf=yQB2y+Q z661}bcfM>#geP*mX>sO^aJrDXgJ@ncpc?{9s+M6tR4MQLy-!fOW%160&10D*2Zj3YuIIEnzwBB z^c$Sj%-BqgsC8h8-3#dqYeJc4$W!d~F^~)~DYX@$%nOvZ92*-##JiogmkSh?86EqV zj5@NcDjDJij)+dk!mB&$cObkfBOV?c1HtsLICCW!W=7ZYzGGw?;LhAqeXd5rd{`;@ z!fyKfYbBnLwAuUf^#>T}0>SYQt4|4JRdvIrq{N}%2yvJlng)IyDtXys{`DWXVbl`{ z>bva5^$AXR>jyIZy^C+l`%44Apw>T6QSF|^GNYTZtT)02xM^v5Rn8&-q--LH%Z2GT zb13$9mxTDatE12WZ`a_SscOx|j#D%)mbKBDqvOli5>E33p(&8MbWfo-Y^iBhPD{RC zoZQ%9O;mlzA!unoCQm8BEsgNr?|o-JzIANAc-4Elcb1m zQWN2JOq=8LT|@8!poty&xeU!Xa%l+Bn!|Gt@W)TCOKM&L6V(QSbwmzXnxw+YINZ?A zxwwsQtB?;cxpX*4njmL@nJ&pvb6&>Lj{r6j=z=s0f4OH-zGaRvy(nIFex)s>HgK@% z&ef>74O=baEZu(HU6exK{kV;2cooSmHl?WR0!gbE-WPqO5Gx03Xv z{3J91np`5%K*Jcoc#$9ju~H_(pM2$eRXN2+2UUlxsm0)k|BV< zty5DCa2%;wh%NCJAfhl`(#KWo8)vbH+U)$h1{S&y17qbyLt)eNu{f6DCCkmljaTo^wY%dnBmJk%`I}|T z1C>MEuO$XuJN;#&t@U1uI(J$iV?+2`gi7Wiuk*V0dL#`X^vH?I%qk9x=K4xN5risF z@7L}OKW8b6v;VH%2gLoqadoPeN5j^+HsE(tpuoH#mraSqz}wOJ^qckYYzoMj=Bvd) z@8x&1iz|AtFme8wc(<|Hn^^3gQ=PV!8b8TFOSnfz@*VkZdbIOOu(j>!I*AteE~`wt zv15A}TMcjU;EL+c=553Iz*bQvhQz{3jLvWTpmqC&eIY9kA;(p6O)SmhqQ>2(>Wj)H zD@X3lv@1AE8@u0eag>yZD0!Spoi&@O96b8xEW8r*nDhFIA`3u5+BR@g(lFk}*2$6j z1s&7m0z^UF`Qw0mp+13z>I3_Y|I7cJn83}PuAR$Y2mUMgfu6U}E}^EFx3P{o zcNaq^5qaw4FJ_lo&Cx5vWScXVPM3dY(}(x9roV2MDD^LBGz0j!J=jS}Qnrwh|M#F+F%Ll-bUOjV ziICMbf$uthD(GOf!@^I7OmwlqrOjIzabW6ETz__7)w|R{z zOW}r?V{Q$c>wg>_%;P=PCD4Ku*|sEA2@MW>LWp7C?m8|!iApdB5G<8Pct=mC%f7)2 zPaF2xdAqH6b<|A(ce+utN8%PKeyg{ppjRPH>#2~NBK(WA>3jK?;^Pa2DfQ%y zjFU~!jc`aT8`NkzXJ@(RL^~gMgffRxi4b9=mzdNG(qQO05p#jc|7@Q8AAMg|#Y|66<6v?w`-S8Q$~z;zH^BS5HBWmv_|0o{A}$*kNHM z#Mr8jsL0WG`i67JhV9D>$m`X!)W5Xa{J)(p=MG8gtc+DnuY9(tG9{?0J2ZLG%!L*6 z#i+bPiV^AtjN7Z|r+Q>P>+eTaaROzrPCqU?r@ORJkX@POn30AF5DJBZ#xT-j<|MK= zf1xl&CAXN(F9Dt-x$}SC9dU$PRPC6xk1 zobe}rUy(^#{c-6yGdBU;pjp`7dKFB=v&iyZ3~7HSP=-iZNLk(pMn3oltMJ^Rfz_p& zDi7^%Iyev8#nx=tT(nassvF~Q74C?r7wo(R2ai*>St>ECNqXc5SL}-sWl>lH`c{7T z?QerRJx?N+Ojge5y#~%k1I(24&G&51H*DB>Opq*bk76DO85bR3cNTStx*2CjYw@3> zE;u5*pAWz2Z|ne`+62OTY22gvyCbL^9=d!&d-ORn*3*P40tGCYWbIj( zwX*W32Q%$1TadB0L&yNW){EJ{UO4%10@5`;1%C)*0J%?9>lpd<I9DTDHt#LvS?;oC1l)3e45Ei`<_6Onrn2Adl&m%P#@t6Wb52cY&%y5oNvCH5sw2M<&GG=5Y7S^AdrZ0+4%eLFd=vJxs*ai@` zRiA>+=pgbj1$=EJuGSx_b8ymRWLK8*dCSR6$Gjm7chgzGPRpG)!an=Bjq(x$AY3%! zv*R2XQ;a8!tj3?OnfI9JUwyyBXF+HEM&GjSA5TR)mqQ9VBN~Mc@+&Iz4_x%8St96& zv#`cJ9p4ASa=iUxCwDFw6E;YjOQ%!^I5;WhKHk5sUB%v>JehKx67)2%-Ye}zG7_7b zX))TUPd-sTP%og&dL5kD^)D%qBQeq&B1r4ACT)lZX0+#BLhhqh#65G*CO~HBk-d^ z(oYf=8B|)pFicz{9kWHf2f#S7Tk!GoO1%2vuq4@d%B?t}IyIu#!u=e;S{+gbDJ|^v zJ;L%K$KY8EqcJFxO;J@=5b#Tgo*!hJ-*35O;sllbiv6?R5D28nyrtUN3~ z?P5$Sa0GLQ9c!_n1NIIY9l$RWk;w51wUhjqYGF*=sZ#hY540FokV=8Ry%zYlq@^bn z+mnE+OC6W*d|W%yA45-yBj5_G&pa5_E|P-e-qT?rMhhb<3xjLoy(+7H)G_D3k9f{l^oX~|0q)s>9h*Z^q~-RGPmMY=A?J9vTm?{xn; zCCQLsVI>B5!TbTKI?kd%QcvSP@i&0kEr#dTe;W*z6h|_j5oX9 zQ+njQNx)H}%}Ehsn)grr&IeRjG?U31LNveP#c7R0F1D zFH;Ta;KVNF14fUjnY-=~;@ao*JH36&6FYpSgQp_=Xi`Gs4Ug-4;z@vYh%OB6G%L{Wou^2%=FdhWatWt~{+eMHca*Odsf*!%-Im&2Fy)q)o54`Hu;d=S&$%*{RU? zA-t)Ew1sV^SMb7XtVirR4f;M-{+pyeA|m+mY@jb_xumW-70KMIQWSYoq*H-)-H?7~ z8$eOmP@SOElV)XIB6m>4KYKB^u+y3EeSsn`LrStVb8m>pwB&aeCU{sv3MGf6fu*+V zkY!WNS@815nTp%1adK!~Nfya3K^~~0j>Z=OMeYs(nC#7!)kAn9jjt3nH|QDWy)hb-NO;XUkC3@*Y*KrV|%Y7>WHE zU3Rh)0#2iJIFw*s;p7zByqrXxE@L6bj2Zre3%dszB zq@9M)AGcjk+wR=Mx*NfBEZ=5<&e?(@*|~w_W;Ax$IQ;oZ)?#W^xz23E)Cn%Nu~KR) zQfNFBiIcTdVv9wgu5GNQCx{YN5|3_;e3Zq-F}@pU zP=jf{$zyVg1=jlBh7mS8jAab)@ymVh%9*gnOxj=5wf|t7&nXNn3glZOphNu?oMOtv zvGbpCIkckolY|FgbAk`>gg&7=p3y_C3MH7n-ztwGS6d;EqQZBV4lB(gk zmW8#BsS^j6&Q=9_)UgA(UXz+zZGW-dJE2(=U(dNnrAg>%Tz#;BzHfbo_v9=N4C%sY zt2pQ7?#-Yq+WEo5Dt6$BTJz!HVR#Co$ig4(-n5}9D9nk7GNXfR>hBJs{MuJi7AlW6 zjq9b0dxEB%wdr4kRF1?)tIU;0lVKA$R<)$3XeE==9=%-eln8wdyj~CZ*ML<5Fr{q# zxq|*mAw%M;OF&5r(<6cBkFU=VHZ2ob#=t<_uTm*FLfXg-!Yk2czcs7Oo4YzC(e*V< zQKf4{7@L%p@=B3ayIqJ;hkhqYbn+1%AMIz=?0&ha0kQgWIe0asJn)73K~Nc_A*+@% zEkI3R``T{r*MW1t%r2k2bU=i-p_t;i!)-9sd<1$hQTbZUG@?X_wJE`LO`VX>)xG?x zk=MV2r1FSTu)2uJRjY6sIpOo}VW!F239q_uuR&FongX9IqE1RzXsltThK&z&ItJ zzY0e4(Si4gpEQ$gH)(O~em2}s7r1=Bd_!VC$S4uST9kvI`{D|jV^1855%HL}r%{HmguJdK6`}+| zYz{+(K7SVXFHd}$ZtYCb`i{QGe83I>B$>HwnTl+gUok*?%kIXW#j>{%tiE}m4*QE5 zqGtCO=)TNH=lk7=iw?R+Q2%z!0!eKG98Vlu;Sv9K1Z{da51Py*1zfn(NC75$4KPWS zGBe*Lj~|Vw5K^LBn~iAYbjT<%ms;aNgw2eO)g>-^$l!m)6&$tm1RZnZ^lEHh@0BWc z)q>^R$*F}GFW%eOpAJ&EB*dr}Vi+>(MecRkRY?^CKc@Cc60biTf}JG&f!AqGy+}Pi zh96$PeRM|SX~0|lO4}*kw`hBz-G~zw+P?P$(VkIGtFvdUZJ#8O!7*nqz3493rwj^% z)gS(>zl}O0B4!?Tu?>_&VypR4^rPyOYtjB^$s1l`zP2r#u@2fJ0(_3b4HS?<%MV58 zUp{BXdFtOYyiDxZ$tTDV_mreU>ego>=-cZ&npUOH+5vlZ>?^>GH1TgcUP}aqv3_x5 zf}G{KpObE$FjN);)G1UBZ2=hg?-Q=|!Wj$xM!`0J07l2hZ3}~*w#H=Mu9elkM#a5x7|F%36M24G>V`*dTJOH%6_E?T)|sa;vDoe=j}FWGh6{brc2m!7KEeg+yP5g zT8d=p_@orpwT7a~wwVX|`}{Th{11PApOg#%jJjvdF~-)aab~X0mJ$BuWNK~}S-6}@ z&WwP@jn6&UieshZ3gmBQV3T_B7tQ@@f1Zk z^rVOk5<8{HaK1<_Q$R9Jjb7QwRr^0_8KRim7YI|GG+8D(tKvGvfwSr=-+bAi{_U!b zfIJ*|9*zh05~WG8$lER0jyOwWdoAd4Q8j-nV^9D`GXjtdK)~UN5Ep09xN1m~jsuoM zX9RCUcv>ivGQb8%u*D8&NDL?#owhJIc-Iif^db8b5Pg68x=bD>1hbpS-uV@BoNULNM@dP-M+#M z4iiH+VdTKsaE)3;lRja@|2dviaEJ!Q0DDasnh|3qJS7Yc1X5tkFzVZ<2*98qn5fqp z2lXEm&!%d}dKlp-S<>@atfhTTT`P1k8EmjJ z#Myg2;9$S%y?+S|w?m_}4R=q2w$k__O=nnPf>-~^ST|O{`K^wL&SHK~(bQ+eQbG~S zg6Y`PP=ph4Ld+9YGignFK;-)t>g(@}j>2?E{99k60@Tkbr~v8jR3?F)+PA4aT2(J1 z0DNkPz#p3v!PDu{uq?x{x2iB-rzRF_e-HZDs>-u1L@Y_3l6p-5e&T(5ZNpZJYi|+E z;Bk%;As?wgf34%Nz*{atUdxDtMV0c8h~Cr7j$S^Q6peii9>d*OFvm}+5K@Ez-cODe z_!8a=5qOnNkMQ=Z9MSbHL= zd`nxc5r5$!%7A!xx}a+p#X*^gKk5LMm8h23JoO;x+56l1s1#p*JWH->gk2uevv+Zy zFyXQj!c-c-T;Us0KVs;Ar(QV{IcG5qAAt=%9Cb(wkX48qz!?`Giii6j3p{L!*xMUX z5^!=OAjF*XuqlY0$HH{RMbJDWOC7c6mUXHFhu?Ci`{x<5KGOmB zYR>pQ=x@LfsNT<}f?8*d9V4pMY~z%O-QHAGwbXm|xMmTs?y!DFN1+<>=Zfs4EzpfJ zL5pq{ZB^mq{%TBOo!w2qZtN&Z3~{j`L&`i+nYl1F9Lm{CTOHM%=2|RXL>*Rk;?De< zl*l>J49or3XR%4MK%p=N%m6@&`>DRYlc#rD>5zrUcL+Hg2~kB&Y1B0R%=yLP3D1`3 z?gb--dM_gOGfMA1@%T+JP`iqto#wsv*kA}PVm@~K@eDCmnz|L-5KJ{Cy7Q)T=BAf$ z538>`JgpEd;>bx+mHkC zt8%h~r7QxkCk+LME$ED|Oi9=Mcg?9#C?nw5E7Qh=a%cmOhZcKsP=`EucBqSbsBcrv zs#fXtW)wO)*JMF&h~hbn;ijuF`I~{D3s-H6?9WKD3O-Z?4A9ns1gSd3+lIH^Lf! zvG3TVSZB#W0(GREJ@jzu`7GY?gGR$$bjeUEY3>Yhy`l=NLveV~1Qw5eEdwu`g?Nq5 zt^~8Usl?dXvJ^l4bkNr2B?-9Gj?p>1?Q=v>J8Wu8$1s^XMx+gZ4t4Vj6T|AaTRQ&( zyJ+}-aQu<7~m*pyLhiWB^PFf70*N_NaT@11gD6d;$NbA>3@U|?sw~d~LdekQK+sFcZ^+?gYimW? zq)GsoDR={?958GWUpnDcK zU&8pUM4)-MmE)3tA3GdHvCl?$o7;ZSL=Zj8j)U}rtKfSu?MvH>0B`DCsaI9`@!^%J zbR0XwD?{X7U&GO9%_1?X)0{uLNTK-oovJWBdrSBChobW$vbI~MRk4o($9oerxdWY6 zZ8qRMx*~WEExPOggh%rNbe3-4!9#{4qyZNLMrTzdW<8i>H2JJ?+XD98W^A3iQ_xn6 ze+O9i#YLp0sJ<^nT+7VmG11zQPVUN*1K=6?%aUTHCe^47NY)eK%q)!gM?Z{P?W)%=KUR;@#h|h*_d@# zqYKaYmo3FIe1_peCfFzO)A;R}Td=XG#?7>1DRV=Y4z48-mL^2%ET4mAAuvu#9 z_9h;l;G~fIx}>5Z`GOf|*$jZ`^4d8`QJ8vt%$zXp(Nav8`0rFyJYrKjM?twvw68)9 zXzti03>;7weU?FwjU(a;t2LD)Qt~wmD#3aPZY-mqOJu;QoXIz4u&UpF8iFO5T>$6% zu5TG%Np0H7XjNg=d%Z{{sT5_@?Pep4?P>i*8JBh=%rGKQ0wIj)*w)3*#$2Rv3F1S^ zp)JbeEdznl=D2D$GO8?&;Cdq~w0e6JOo)kp=+2B!mqI4t$d9bAK6&EFc^%0JNV1^C|#5gU3A?Ltn{* zzD_=yc%!K`GWJF?DICMQnE1*~fRMjv_a&S|An~TH=Ex2X5o-(;#%0;!XjM60fC)%G zfpr_FAQY+WCKDlts?uh?uMA~_GE@2nz=gQAipVUX1c1kwS6mOAJjqBVC+G%}SrI`K zkqPPO+k0p;vU`Hx6lH7d!CH6WHo<1`w zig!+eXtPLy$rDLc(Vhah^$#DgfNfGw>jy09KV{EF&$j>>gJH%fJ=)>`L|~!#!EvOlMcW^e2B<<*K>z0K=m@Z_>fHe)-u7ENEW#x)FBzMo zoP@^jWCR2+jM{0{eb*N{B%kPDuh{E3Cf7SgmF;Cm>e=t@yyAG^qKs-9EmM9;49VEc z8Ib>e&Waf|JR@04X#=wd9;}VoyEr8l&I~LLD4vSED;ArpW~;>8r#1}F&sLqU{*hnb z_IGH1Uz`f_@kd(J{;BerE}`@g5(5N$So%gK^9Q0ig(5i4P zd#^`;EH(P;1BQ+;z}Mdl*L&EXP#l$lfQ=yzJ~2?}IJ%4}92G)-)B-mJ&%@Hv$&{C$ zO-Zy!WwWnd(%gtMzgO9kPLc1SoSowuO?Vcq%#$7-zOt94u05$ zMq^xH5ZjPB_L>pS-I)xuwnbU~D<#G$X;Q~QC0@9xWBQ#Q%atQ~JA zaKOM^^h5sbz0j069)Gu5(9_s)qcoJwDM7-?rF`$%JjnT5EeaEkvNtSiv_&#$;RFX( zHMz!p?wu`|yy$>n`})~2Uyp!F3Fd5-ulDT9lnsy?g$$WemY%{b{wg1Nix@V-N~mQC ziqd#s2xm>Ua4))+Rbnq1ic*1!iv8veD-|6|&BCn5AU#DFKaL%iH;SY`*oXhk-veyD zj4UCF`$K_1&l{6haQ(mC8fHIITq7dI+eo;a+i@1?yl>A@Bm?HYgQ|Kw5L_&ByJ%RZ zpFqU1Dsxhr8FDrz1%u`6dg(4z(+Iv9!W(&yK#X#aY)T0+j|@pZOZhw~ch6@%K~4b=QIy=Iu17-VO_e zz1*sLcq^m!9Ld8Tl@U~1bMCimuWPhW-j+WuzR>t_PVNq z<087TK#!?95dFt!|VaM)+KEgH^AwyadRPaeh<*)1R?{y78U)|5T zEk$fpVEi(%Wvhk#?mV@%AjUpguxQ%k&%_cdi;C~7A2CUh6t76-ykK1AVqNNQ5*cIoeVOQ){KTfsG^eEWMm-25)jQ3~K2Czth$0L>Z18VReH39UtE+3fAq93!x~$P|EN%&kq+8vtf# zA3@(NRt>2Mcjlnl;@QPU?Fh|6ye&^l3384j+WQZ~ZBpi0Q(u^|>x9zn zb%7cv$PM3wayH06Nit#_9B>O7NnkBM_#oCoJ_miL*>^+yLh?2{W2B>N|g0k&UOo>YIgo+{+~ zJs0m?6`l+O4Y8PyCoLQVt<34q+(NHA0r+SPrv`esvRq2hkM>39tl$D(WVrEiXg`A$ zPAY|*+7?(PU*>p8<_|ZWsH|I zFn)R3_icYZ>_0LW<$lF6S`Li)^by{o!NyVzFhQ0SR!v5L?Zt(EyahxQ81o7AzUr>f zNaCCg!808JYdAMh;)u!K4iS>2JHkL(wxFVzy*)`He|WG`knlnChl8Y2b%`L#9h=OubS7-C6Bfjt)LKU>VZGk#ZM;mNT{ zFF_+v&MK}C=v7x~^w@nE^jV#7Jz^?kJ|cRcHnh=52z>b)nHpkx-okSb(cdn^?b!Hm z9x9wiuuo3Y=Xxy$>U%W6NT$%KjxwR|=V0Ly&%cak0UxdL=t)GAEeP z3{iA0sM6w~XKfcj6l+<*v}|`l=M`&eKrd$rlV5K}mMgze%}2F!Tx1D9>0`LP*&I@} z_PZ-qa*Fg~wtk&*fvzl(kpv`FV{j)5GEQ~aMn&{O$8WML%Lxyou;Wz~LXdqrlIq;~ImzpnoG0XMn5CDfZ2yye^qHO}#k zj8b2Y-3zU{-j#1l+W0f>*SmhY*(KbYaT>~>xtwS8$W(W8G3DK?)~k{Ayw(>XKOzVJ zsf?sHGIHy88}dfu%#5vP3I|yddk8MMy8b6`{@nkOoaltqLSByxVj_OQs5YSHPs?7*)0S}->eSPkMF{gKlsB4f?buY?jOBx9?_{!+`N;tVPiU<_<@!~scWh#a z`0{n;b`hDgj16hBoH}2I%^5ud&%K|PiNcd;8Luk~GXw3Ke=Sk|8$EbcNDgeRt2Jc& zq#jNP^D(yegb#s(^gK-1Ld6J=)hJLI(v`9NO&T7PYz%L zsP5A$6Nv|*qdsOdU#P|T9&uqOSiChc_A9vaUY~+KE7HaMHDn2U{Giv3ybS^h5Yd+7 zh-hZvBBb{c_MD0b@j0(6Jtw~Q)bT_*tum6eh4e$BAtdukjPl)o6`=cNF8;171KwyJEE4Mhhd5Cgd6v{w;Yoi%rYJ-655DJtb zo=q0jD*N5GY}!UnG1nO=1kLS$Q_^)xnt0}=@<0i5dov3$bOW`7DXgp~zb_?6V$m&_ zuYl^kk0q|=#*(OHBFH)kQ2LU?d>q)lZP;`Ii3J0?lSgezs^JdFQeRIE1T&c}q*(?k zgfUOyTOu+84UW_~X0R{=3>B=ssb!!vjNB!lJjb5_$?>ub%rB#}#`c@_!JY^sPe}n@ z_uK&ynRIUhYfBw~T=^)y@RJbZ)mNM$_Qzng<_9a5Bhh$@rEz+$5L+omtnlxEJApr%`y`)6_$&1uEu!dxlG=ct|=9LBL6W2X#s7c`xA0q3m&Lo2!>(8v`@Jx(x@mhslik*}gH+cp+Nm8iqWQfG){+hVv~R1M zVsUmQ8?&l!E7!@|K#g!2=6<2L05Q_bSQkXTTu80r06gj)?qThDJ~5;`*tsFUFHh*5 zWr4u;lmC@tj6XA;82^=+CH$uudcqV^VkG&rAawD7+4(JcaN{k;mK@ZQ5sL8kPU%T- z;qteGz31SEf7K2X?wfUrf3nd(?e4YSM&sd1mI^ufA4X|iR^ln8zH$_9gCFFb6K(yHQgCcFed(7+fodJ9Bf+A9xWP`sjmZ8Z{CT` zaOw!=yHSU&ealjq@G9Qq71&8Rp$`hu)irKr@9NCK^xm40&oikT7iI3XFlJ=#A#q=D z37`yEu-fbzf$(pti~X`8!BVj_rTV+)O4`~8tXk5(Q(J?+bWzoNqhijDjO%QHg>b$P zwJeb^Tu6{Haq8NeIJsfkQ|3fCpsz{>{p8RT^sjXZ;K1vZ7nm5IT4aR2nQjt6%~?8b z7PO=nD2jUz}aFu`_-8_aBSQ{pQd{3|EaDI$%i0uXI7EsO&W zb7h@i-zsp|^fg>0 zz{i^CU!y^Ua)1KjE$mzyC}%dpDA%Tn`X%2npOCM&Kzxwx7klVbXBE=M-UcY&Zj%Gg5K(i# z#*=a@tMxS;XmWttD$1J`Nif_CNR8 z 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
{G?cQ_foxMc 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 k_CI{B|9X&ykC|5?RtI-TJXTu7A>ebu Date: Mon, 31 Oct 2022 22:39:15 -0700 Subject: [PATCH 47/47] fixup docs --- docs/cli.md | 4 ++-- .../powerbi-report-server.md | 0 .../powerbi-report-server_recipe.yml | 0 .../source/powerbi_report_server/report_server.py | 7 ++++--- .../powerbi_report_server/test_powerbi_report_server.py | 4 +++- 5 files changed, 9 insertions(+), 6 deletions(-) rename metadata-ingestion/docs/sources/{powerbi-report-server => powerbi}/powerbi-report-server.md (100%) rename metadata-ingestion/docs/sources/{powerbi-report-server => powerbi}/powerbi-report-server_recipe.yml (100%) diff --git a/docs/cli.md b/docs/cli.md index e72e4334f5766..9e7de977cd3f1 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -100,8 +100,8 @@ We use a plugin architecture so that you can install only the dependencies you a | [trino](./generated/ingestion/sources/trino.md) | `pip install 'acryl-datahub[trino]'` | Trino source | | [starburst-trino-usage](./generated/ingestion/sources/trino.md) | `pip install 'acryl-datahub[starburst-trino-usage]'` | Starburst Trino usage statistics source | | [nifi](./generated/ingestion/sources/nifi.md) | `pip install 'acryl-datahub[nifi]'` | Nifi source | -| [powerbi](./generated/ingestion/sources/powerbi.md) | `pip install 'acryl-datahub[powerbi]'` | Microsoft Power BI source | -| [powerbi-report-server](./generated/ingestion/sources/powerbi-report-server.md) | `pip install 'acryl-datahub[powerbi-report-server]'` | Microsoft Power BI Report Server source | +| [powerbi](./generated/ingestion/sources/powerbi.md#module-powerbi) | `pip install 'acryl-datahub[powerbi]'` | Microsoft Power BI source | +| [powerbi-report-server](./generated/ingestion/sources/powerbi.md#module-powerbi-report-server) | `pip install 'acryl-datahub[powerbi-report-server]'` | Microsoft Power BI Report Server source | ### Sinks diff --git a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server.md b/metadata-ingestion/docs/sources/powerbi/powerbi-report-server.md similarity index 100% rename from metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server.md rename to metadata-ingestion/docs/sources/powerbi/powerbi-report-server.md diff --git a/metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server_recipe.yml b/metadata-ingestion/docs/sources/powerbi/powerbi-report-server_recipe.yml similarity index 100% rename from metadata-ingestion/docs/sources/powerbi-report-server/powerbi-report-server_recipe.yml rename to metadata-ingestion/docs/sources/powerbi/powerbi-report-server_recipe.yml diff --git a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py index 9757fe89bcb9d..878d084a0eb66 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py +++ b/metadata-ingestion/src/datahub/ingestion/source/powerbi_report_server/report_server.py @@ -460,13 +460,14 @@ def report_dropped(self, view: str) -> None: self.filtered_reports.append(view) -@platform_name("PowerBI Report Server") +@platform_name("PowerBI") @config_class(PowerBiReportServerDashboardSourceConfig) -@support_status(SupportStatus.UNKNOWN) +@support_status(SupportStatus.INCUBATING) @capability(SourceCapability.OWNERSHIP, "Enabled by default") class PowerBiReportServerDashboardSource(Source): """ - This plugin extracts the following: + Use this plugin to connect to [PowerBI Report Server](https://powerbi.microsoft.com/en-us/report-server/). + It extracts the following: Metadata that can be ingested: - report name diff --git a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py index adadbe31416cf..2dedfb091392e 100644 --- a/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py +++ b/metadata-ingestion/tests/integration/powerbi_report_server/test_powerbi_report_server.py @@ -177,7 +177,9 @@ def default_source_config(): @freeze_time(FROZEN_TIME) @mock.patch("requests_ntlm.HttpNtlmAuth") def test_powerbi_ingest(mock_msal, pytestconfig, tmp_path, mock_time, requests_mock): - test_resources_dir = pytestconfig.rootpath / "tests/integration/powerbi_report_server" + test_resources_dir = ( + pytestconfig.rootpath / "tests/integration/powerbi_report_server" + ) register_mock_api(request_mock=requests_mock)