import { WebPartContext } from '@microsoft/sp-webpart-base' ; import { dateAdd } from '@pnp/common' ; import { QueryPropertyValueType , SearchResult , SortDirection , sp } from '@pnp/sp' ; import * as cleanDeep from 'clean-deep' ; import { IGraphGroup , IPortfolioConfiguration , ISPProjectItem , ISPUser } from 'interfaces' ; import { ChartConfiguration , ChartData , ChartDataItem , DataField , ProjectListModel , TimelineContentListModel , SPChartConfigurationItem , SPContentType } from 'models' ; import MSGraph from 'msgraph-helper' ; import { format } from 'office-ui-fabric-react/lib/Utilities' ; import * as strings from 'PortfolioWebPartsStrings' ; import { DataSource , PortfolioOverviewView } from 'pp365-shared/lib/models' ; import { DataSourceService } from 'pp365-shared/lib/services/DataSourceService' ; import { PortalDataService } from 'pp365-shared/lib/services/PortalDataService' ; import HubSiteService from 'sp-hubsite-service' ; import _ from 'underscore' ; import { IFetchDataForViewItemResult } from './IFetchDataForViewItemResult' ; import { DEFAULT_SEARCH_SETTINGS } from './types' ; export class DataAdapter { private _portalDataService : PortalDataService ; private _dataSourceService : DataSourceService ; constructor ( public context ) { this . _portalDataService = new PortalDataService ( ) . configure ( { urlOrWeb : context . pageContext . web . absoluteUrl } ) ; } /**
     * Configuring the DataAdapter enabling use
     * of the DataSourceService.
     */ public async configure ( ) { if ( this . _dataSourceService ) return this ; const { web } = await HubSiteService . GetHubSite ( sp , this . context . pageContext ) ; this . _dataSourceService = new DataSourceService ( web ) ; return this ; } param view param configuration param chartConfigurationListName param siteId public async fetchChartData ( view , configuration , chartConfigurationListName , siteId ) { try { const [ chartItems , contentTypes ] = await Promise . all ( [ sp . web . lists . getByTitle ( chartConfigurationListName ) . items . select ( ... Object . keys ( new SPChartConfigurationItem ( ) ) ) . get < SPChartConfigurationItem [ ] > ( ) , sp . web . lists . getByTitle ( chartConfigurationListName ) . contentTypes . select ( ... Object . keys ( new SPContentType ( ) ) ) . get < SPContentType [ ] > ( ) ] ) ; const charts = chartItems . map ( ( item ) => { const fields = item . GtPiFieldsId . map ( ( id ) => { const fld = _ . find ( configuration . columns , ( f ) => f . id === id ) ; return new DataField ( fld . name , fld . fieldName , fld . dataType ) ; } ) ; const chart = new ChartConfiguration ( item , fields ) ; return chart ; } ) ; const items = ( await this . fetchDataForView ( view , configuration , siteId ) ) . map ( ( i ) => new ChartDataItem ( i . Title , i ) ) ; const chartData = new ChartData ( items ) ; return { charts , chartData , contentTypes } ; } catch ( error ) { throw error ; } } public async getPortfolioConfig ( ) { let [ columnConfig , columns , views , viewsUrls , columnUrls ] = await Promise . all ( [ this . _portalDataService . getProjectColumnConfig ( ) , this . _portalDataService . getProjectColumns ( ) , this . _portalDataService . getPortfolioOverviewViews ( ) , this . _portalDataService . getListFormUrls ( 'PORTFOLIO_VIEWS' ) , this . _portalDataService . getListFormUrls ( 'PROJECT_COLUMNS' ) ] ) ; columns = columns . map ( ( col ) => col . configure ( columnConfig ) ) ; const refiners = columns . filter ( ( col ) => col . isRefinable ) ; views = views . map ( ( view ) => view . configure ( columns ) ) ; return { columns , refiners , views , viewsUrls , columnUrls } ; } description param view param configuration param siteId returns { Promise < IFetchDataForViewItemResult [ ] > } memberof public async fetchDataForView ( view , configuration , siteId ) { const isCurrentUserInManagerGroup = await this . isUserInGroup ( strings . PortfolioManagerGroupName ) ; if ( isCurrentUserInManagerGroup ) { return await this . fetchDataForManagerView ( view , configuration , siteId ) ; } else { return await this . fetchDataForRegularView ( view , configuration , siteId ) ; } } param view param configuration param siteId param siteIdProperty public async fetchDataForRegularView ( view , configuration , siteId , siteIdProperty = 'GtSiteIdOWSTEXT' ) { try { const { projects , sites , statusReports } = await this . _fetchDataForView ( view , configuration , siteId , siteIdProperty ) ; const items = sites . map ( ( site ) => { const [ project ] = projects . filter ( ( res ) => res [ siteIdProperty ] === site [ 'SiteId' ] ) ; const [ statusReport ] = statusReports . filter ( ( res ) => res [ siteIdProperty ] === site [ 'SiteId' ] ) ; return { ... statusReport , ... project , Title : site . Title , Path : site . Path , SiteId : site [ 'SiteId' ] } ; } ) ; return items ; } catch ( err ) { throw err ; } } description param view param configuration param siteId param siteIdProperty returns { Promise < IFetchDataForViewItemResult [ ] > } memberof public async fetchDataForManagerView ( view , configuration , siteId , siteIdProperty = 'GtSiteIdOWSTEXT' ) { try { const { projects , sites , statusReports } = await this . _fetchDataForView ( view , configuration , siteId , siteIdProperty ) ; const items = projects . map ( ( project ) => { const [ statusReport ] = statusReports . filter ( ( res ) => res [ siteIdProperty ] === project [ siteIdProperty ] ) ; const [ site ] = sites . filter ( ( res ) => res [ 'SiteId' ] === project [ siteIdProperty ] ) ; return { ... statusReport , ... project , Path : site && site . Path , SiteId : project [ siteIdProperty ] } ; } ) ; return items ; } catch ( err ) { throw err ; } } param view param configuration param siteId param siteIdProperty private async _fetchDataForView ( view , configuration , siteId , siteIdProperty = 'GtSiteIdOWSTEXT' ) { let [ { PrimarySearchResults : projects } , { PrimarySearchResults : sites } , { PrimarySearchResults : statusReports } ] = await Promise . all ( [ sp . search ( { ... DEFAULT_SEARCH_SETTINGS , QueryTemplate : view . searchQuery , SelectProperties : [ ... configuration . columns . map ( ( f ) => f . fieldName ) , siteIdProperty ] } ) , sp . search ( { ... DEFAULT_SEARCH_SETTINGS , QueryTemplate : `DepartmentId:{${ siteId }} contentclass:STS_Site` , SelectProperties : [ 'Path' , 'Title' , 'SiteId' ] } ) , sp . search ( { ... DEFAULT_SEARCH_SETTINGS , QueryTemplate : `DepartmentId:{${ siteId }} ContentTypeId:0x010022252E35737A413FB56A1BA53862F6D5* GtModerationStatusOWSCHCS:Publisert` , SelectProperties : [ ... configuration . columns . map ( ( f ) => f . fieldName ) , siteIdProperty ] , Refiners : configuration . refiners . map ( ( ref ) => ref . fieldName ) . join ( ',' ) } ) ] ) ; projects = projects . map ( ( item ) => cleanDeep ( { ... item } ) ) ; sites = sites . map ( ( item ) => cleanDeep ( { ... item } ) ) ; statusReports = statusReports . map ( ( item ) => cleanDeep ( { ... item } ) ) ; sites = sites . filter ( ( site ) => projects . filter ( ( res ) => res [ siteIdProperty ] === site [ 'SiteId' ] ) . length === 1 ) ; return { projects , sites , statusReports } ; } param siteId param siteIdProperty param costsTotalProperty param budgetTotalProperty public async _fetchDataForTimelineProject ( siteId ) { const siteIdProperty = 'GtSiteIdOWSTEXT' ; const costsTotalProperty = 'GtCostsTotalOWSCURR' ; const budgetTotalProperty = 'GtBudgetTotalOWSCURR' ; let [ { PrimarySearchResults : statusReports } ] = await Promise . all ( [ sp . search ( { ... DEFAULT_SEARCH_SETTINGS , QueryTemplate : `DepartmentId:{${ this . context . pageContext . legacyPageContext . hubSiteId }} ${ siteIdProperty }:{${ siteId }}
        ContentTypeId:0x010022252E35737A413FB56A1BA53862F6D5* GtModerationStatusOWSCHCS:Publisert` , SelectProperties : [ costsTotalProperty , budgetTotalProperty , siteIdProperty ] } ) ] ) ; statusReports = statusReports . map ( ( item ) => cleanDeep ( { ... item } ) ) ; return { statusReports } ; } /**
     *  Fetches items from timelinecontent list
     *
     * * Fetching list items
     * * Maps the items to TimelineContentListModel
     */ public async _fetchTimelineContentItems ( ) { let [ timelineItems ] = await Promise . all ( [ sp . web . lists . getByTitle ( strings . TimelineContentListName ) . items . select ( 'Title' , 'TimelineType' , 'GtStartDate' , 'GtEndDate' , 'GtBudgetTotal' , 'GtCostsTotal' , 'SiteIdLookup/Title' , 'SiteIdLookup/GtSiteId' ) . expand ( 'SiteIdLookup' ) . get ( ) ] ) ; timelineItems = timelineItems . map ( ( item ) => { if ( item . SiteIdLookup ?. Title ) { const model = new TimelineContentListModel ( item . SiteIdLookup ?. GtSiteId , item . SiteIdLookup ?. Title , item . Title , item . TimelineType , item . GtStartDate , item . GtEndDate , item . GtBudgetTotal , item . GtCostsTotal ) ; return model ; } } ) . filter ( ( p ) => p ) ; return { timelineItems } ; } param rowLimit param sortProperty param sortDirection public async fetchProjectSites ( rowLimit , sortProperty , sortDirection ) { const response = await sp . search ( { Querytext : `DepartmentId:{${ this . context . pageContext . legacyPageContext . hubSiteId }} contentclass:STS_Site` , TrimDuplicates : false , RowLimit : rowLimit , SelectProperties : [ 'Title' , 'Path' , 'SiteId' , 'Created' ] , SortList : [ { Property : sortProperty , Direction : sortDirection } ] , Properties : [ { Name : 'EnableDynamicGroups' , Value : { BoolVal : true , QueryPropertyValueTypeIndex : QueryPropertyValueType . BooleanType } } ] } ) ; return response . PrimarySearchResults . filter ( ( site ) => this . context . pageContext . legacyPageContext . hubSiteId !== site [ 'SiteId' ] ) ; } param items param groups param photos param users private _mapProjects ( items , groups , users ) { const projects = items . map ( ( item ) => { const [ group ] = groups . filter ( ( grp ) => grp . id === item . GtGroupId ) ; const [ owner ] = users . filter ( ( user ) => user . Id === item . GtProjectOwnerId ) ; const [ manager ] = users . filter ( ( user ) => user . Id === item . GtProjectManagerId ) ; const model = new ProjectListModel ( item . GtSiteId , item . GtGroupId , group ?. displayName ?? item . Title , item . GtSiteUrl , item . GtProjectPhaseText , item . GtStartDate , item . GtEndDate , manager , owner , ! ! group ) ; return model ; } ) . filter ( ( p ) => p ) ; return projects ; } /**
     * Fetch enriched projects
     * * Fetching project list items
     * * Graph groups
     * * Site users
     * * Combines the data
     */ public async fetchEncrichedProjects ( ) { await MSGraph . Init ( this . context . msGraphClientFactory ) ; const [ items , groups , users ] = await Promise . all ( [ sp . web . lists . getByTitle ( strings . ProjectsListName ) . items . select ( 'GtGroupId' , 'GtSiteId' , 'GtSiteUrl' , 'GtProjectOwnerId' , 'GtProjectManagerId' , 'GtProjectPhaseText' , 'GtStartDate' , 'GtEndDate' , 'Title' ) . filter ( "GtProjectLifecycleStatus ne 'Avsluttet'" ) . orderBy ( 'Title' ) . top ( 500 ) . usingCaching ( ) . get < ISPProjectItem [ ] > ( ) , MSGraph . Get < IGraphGroup [ ] > ( '/me/memberOf/$/microsoft.graph.group' , [ 'id' , 'displayName' ] , "groupTypes/any(a:a%20eq%20'unified')" ) , sp . web . siteUsers . select ( 'Id' , 'Title' , 'Email' ) . usingCaching ( { key : 'fetchencrichedprojects_siteusers' , storeName : 'session' , expiration : dateAdd ( new Date ( ) , 'minute' , 15 ) } ) . get < ISPUser [ ] > ( ) ] ) ; const projects = this . _mapProjects ( items , groups , users ) ; return projects ; } public param groupName returns { Promise < boolean > } memberof public async isUserInGroup ( groupName ) { try { const [ siteGroup ] = await sp . web . siteGroups . select ( 'CanCurrentUserViewMembership' , 'Title' ) . filter ( `Title eq '${ groupName }'` ) . get ( ) ; return siteGroup && siteGroup . CanCurrentUserViewMembership ; } catch ( error ) { return false ; } } param queryTemplate param selectProperties private async _fetchItems ( queryTemplate , selectProperties ) { const response = await sp . searchWithCaching ( { QueryTemplate : queryTemplate , Querytext : '*' , RowLimit : 500 , TrimDuplicates : false , SelectProperties : [ ... selectProperties , 'Path' , 'SPWebURL' , 'SiteTitle' ] } ) ; return response . PrimarySearchResults ; } param name param selectProperties public async fetchItemsWithSource ( name , selectProperties ) { const dataSrc = await this . _dataSourceService . getByName ( name ) ; if ( ! dataSrc ) { throw new Error ( format ( strings . DataSourceNotFound , name ) ) ; } try { const items = await this . _fetchItems ( dataSrc . searchQuery , selectProperties ) ; return items ; } catch ( error ) { throw new Error ( format ( strings . DataSourceError , name ) ) ; } } param category public fetchDataSources ( category ) { try { return this . _dataSourceService . getByCategory ( category ) ; } catch ( error ) { throw new Error ( format ( strings . DataSourceCategoryError , category ) ) ; } } }