import { ISPTermStorePickerService , IPnPTermStorePickerServiceProps , ITermStore , ITermSet , TermStorePickerServiceHelper , ITerm , IGroup , ITermSets } from "./ISPTermStorePickerService" ; import { BaseComponentContext } from '@microsoft/sp-component-base' ; import { taxonomy , ITermStore as PnPTermStore , ITermStoreData , StringMatchOption , ITermSet as PnPTermSet , ITermSetData , ITermGroupData , ITermGroup as PnPTermGroup , ITermData , ITerm as PnPTerm , Terms } from "@pnp/sp-taxonomy" ; import { IPickerTerm } from './../propertyFields/termPicker/IPropertyFieldTermPicker' ; import { Environment , EnvironmentType } from "@microsoft/sp-core-library" ; import SPTermStoreMockHttpClient from "./SPTermStorePickerMockService" ; /**
 * Term Store Picker Service implementation that uses @pnp/sp-taxonomy to work with taxonomy service
 */ export default class PnPTermStorePickerService implements ISPTermStorePickerService { private readonly _termSetCollectionObjectType : string = 'SP.Taxonomy.TermSetCollection' ; private readonly _termGroupCollectionObjectType : string = 'SP.Taxonomy.TermGroupCollection' ; private _pnpTermStores : ( ITermStoreData & PnPTermStore ) [ ] ; private _pnpGroups : { [ termStoreId ] : ( ITermGroupData & PnPTermGroup ) [ ] ; } = { } ; constructor ( private props , private context ) { taxonomy . setup ( { spfxContext : context } ) ; } /**
     * Gets term stores from the taxonomy service
     */ public async getTermStores ( ) { if ( Environment . type === EnvironmentType . Local ) { return await SPTermStoreMockHttpClient . getTermStores ( this . context . pageContext . web . absoluteUrl ) as ITermStore [ ] ; } else { await this . _ensureTermStores ( ) ; const result = [ ] ; this . _pnpTermStores . forEach ( pnpTermStore => { const pnpTermStoreAny = pnpTermStore as any ; result . push ( { _ObjectType_ : 'SP.Taxonomy.TermStore' , _ObjectIdentity_ : pnpTermStoreAny . _ObjectIdentity_ , Id : pnpTermStore . Id , Name : pnpTermStore . Name , Groups : { _ObjectType_ : this . _termGroupCollectionObjectType , _Child_Items_ : this . _pnpGroups [ pnpTermStore . Id ] . map ( g => { return this . _pnpTermGroup2TermGroup ( g , pnpTermStore ) ; } ) } } ) ; } ) ; return result ; } } param searchText public async searchTermsByName ( searchText ) { if ( Environment . type === EnvironmentType . Local ) { return SPTermStoreMockHttpClient . searchTermsByName ( searchText ) ; } else { if ( this . props . limitByTermsetNameOrID ) { return this . _searchTermsByTermSet ( searchText ) ; } else if ( this . props . limitByGroupNameOrID ) { return this . _searchTermsByGroup ( searchText ) ; } else { return this . _searchAllTerms ( searchText ) ; } } } /**
     * Gets term sets from the stores
     */ public async getTermSets ( ) { let termSets = [ ] ; if ( Environment . type === EnvironmentType . Local ) { const termStores = await SPTermStoreMockHttpClient . getTermStores ( this . context . pageContext . web . absoluteUrl ) as ITermStore [ ] ; if ( termStores && termStores . length > 0 ) { const ts = termStores [ 0 ] ; if ( ts . Groups && ts . Groups . _Child_Items_ ) { for ( const group of ts . Groups . _Child_Items_ ) { if ( group . TermSets && group . TermSets . _Child_Items_ ) { if ( this . props . limitByTermsetNameOrID ) { for ( const termSet of group . TermSets . _Child_Items_ ) { if ( termSet . Name === this . props . limitByTermsetNameOrID || termSet . Id . indexOf ( this . props . limitByTermsetNameOrID ) !== - 1 ) { termSets = [ ... termSets , termSet ] ; } } } else { termSets = [ ... termSets , ... group . TermSets . _Child_Items_ ] ; } } } } } return termSets ; } await this . _ensureTermStores ( ) ; for ( let i = 0 , len = this . _pnpTermStores . length ; i < len ; i ++ ) { const pnpTermStore = this . _pnpTermStores [ i ] ; if ( this . props . limitByTermsetNameOrID ) { let pnpTermSets = await this . _getPnPTermSetsByNameOrId ( pnpTermStore , this . props . limitByTermsetNameOrID ) ; const groupsBatch = taxonomy . createBatch ( ) ; for ( let termSetIdx = 0 , termSetLen = pnpTermSets . length ; termSetIdx < termSetLen ; termSetIdx ++ ) { const pnpTermSet = pnpTermSets [ termSetIdx ] ; const termSet = this . _pnpTermSet2TermSet ( pnpTermSet , '' ) ; termSets . push ( termSet ) ; pnpTermSet . group . inBatch ( groupsBatch ) . usingCaching ( ) . get ( ) . then ( pnpTermGroup => { termSet . Group = TermStorePickerServiceHelper . cleanGuid ( pnpTermGroup . Id ) ; } ) ; } await groupsBatch . execute ( ) ; } else { let pnpGroups ; if ( this . props . limitByGroupNameOrID ) { const pnpGroup = this . _getPnPTermGroupsByNameOrId ( pnpTermStore . Id , this . props . limitByGroupNameOrID ) ; pnpGroups = [ ] ; if ( pnpGroup ) { pnpGroups . push ( pnpGroup ) ; } } else { pnpGroups = this . _pnpGroups [ pnpTermStore . Id ] ; } const batch = taxonomy . createBatch ( ) ; pnpGroups . forEach ( pnpGroup => { pnpGroup . termSets . inBatch ( batch ) . usingCaching ( ) . get ( ) . then ( pnpTermSets => { termSets = [ ... termSets , ... pnpTermSets . map ( pnpTermSet => { return this . _pnpTermSet2TermSet ( pnpTermSet , TermStorePickerServiceHelper . cleanGuid ( pnpGroup . Id ) ) ; } ) ] ; } ) ; } ) ; await batch . execute ( ) ; } } return termSets ; } param termSet public async getAllTerms ( termSet ) { if ( Environment . type === EnvironmentType . Local ) { return SPTermStoreMockHttpClient . getAllTerms ( ) ; } await this . _ensureTermStores ( ) ; const pnpTermStores = this . _pnpTermStores ; for ( const pnpTermStore of pnpTermStores ) { const termsResult = await this . _tryGetAllTerms ( pnpTermStore , termSet ) . catch ( ( error ) => { } ) ; if ( ! termsResult ) { continue ; } const pnpTerms = termsResult as ( ITermData & PnPTerm ) [ ] ; const resultTerms = [ ] ; const labelsBatch = taxonomy . createBatch ( ) ; for ( let termIdx = 0 , termsLen = pnpTerms . length ; termIdx < termsLen ; termIdx ++ ) { const pnpTerm = pnpTerms [ termIdx ] ; const term = ( pnpTerm as any ) as ITerm ; term . Id = TermStorePickerServiceHelper . cleanGuid ( term . Id ) ; term . PathDepth = term . PathOfTerm . split ( ';' ) . length ; term . TermSet = termSet ; resultTerms . push ( term ) ; if ( this . props . includeLabels ) { pnpTerm . labels . inBatch ( labelsBatch ) . usingCaching ( ) . get ( ) . then ( labels => { term . Labels = labels . map ( label => label . Value ) ; } ) ; } } if ( this . props . includeLabels ) { await labelsBatch . execute ( ) ; } return TermStorePickerServiceHelper . sortTerms ( resultTerms ) ; } } param group public async getGroupTermSets ( group ) { await this . _ensureTermStores ( ) ; const pnpTermStore = this . _pnpTermStores . filter ( ts => TermStorePickerServiceHelper . cleanGuid ( ts . Id ) === group . TermStore . Id ) [ 0 ] ; const pnpGroups = this . _pnpGroups [ pnpTermStore . Id ] . filter ( gr => TermStorePickerServiceHelper . cleanGuid ( gr . Id ) === group . Id ) ; if ( ! pnpGroups || ! pnpGroups . length ) { return { _ObjectType_ : this . _termSetCollectionObjectType , _Child_Items_ : [ ] } ; } const pnpGroup = pnpGroups [ 0 ] ; let pnpTermSets ; if ( this . props . limitByTermsetNameOrID ) { const isGuid = TermStorePickerServiceHelper . isGuid ( this . props . limitByTermsetNameOrID ) ; if ( isGuid ) { pnpTermSets = [ await pnpGroup . termSets . getById ( this . props . limitByTermsetNameOrID ) . usingCaching ( ) . get ( ) ] ; } else { pnpTermSets = [ await pnpGroup . termSets . getByName ( this . props . limitByTermsetNameOrID ) . usingCaching ( ) . get ( ) ] ; } } else { pnpTermSets = await pnpGroup . termSets . usingCaching ( ) . get ( ) ; } const result = { _ObjectType_ : this . _termSetCollectionObjectType , _Child_Items_ : pnpTermSets . map ( pnpTermSet => { return this . _pnpTermSet2TermSet ( pnpTermSet , TermStorePickerServiceHelper . cleanGuid ( pnpGroup . Id ) ) ; } ) } ; return result ; } param pnpTermStore param termSet private _tryGetAllTerms ( pnpTermStore , termSet ) { return new Promise < ( ITermData & PnPTerm ) [ ] > ( ( resolve , reject ) => { pnpTermStore . getTermSetById ( termSet . Id ) . terms . usingCaching ( ) . get ( ) . then ( ( pnpTerms ) => { resolve ( pnpTerms ) ; } , ( error ) => { reject ( error ) ; } ) ; } ) ; } param searchText private async _searchTermsByTermSet ( searchText ) { if ( Environment . type === EnvironmentType . Local ) { return SPTermStoreMockHttpClient . searchTermsByName ( searchText ) ; } else { await this . _ensureTermStores ( ) ; let returnTerms = [ ] ; const pnpTermStores = this . _pnpTermStores ; for ( let i = 0 , len = pnpTermStores . length ; i < len ; i ++ ) { const pnpTermStore = pnpTermStores [ i ] ; const pnpTermSets = await this . _getPnPTermSetsByNameOrId ( pnpTermStore , this . props . limitByTermsetNameOrID ) ; returnTerms . push ( ... await this . _searchTermsInTermSets ( pnpTermStore , pnpTermSets , searchText ) ) ; } return returnTerms ; } } param searchText private async _searchTermsByGroup ( searchText ) { if ( Environment . type === EnvironmentType . Local ) { return SPTermStoreMockHttpClient . searchTermsByName ( searchText ) ; } else { await this . _ensureTermStores ( ) ; const groupNameOrID = this . props . limitByGroupNameOrID ; let returnTerms = [ ] ; const pnpTermStores = this . _pnpTermStores ; for ( let i = 0 , len = pnpTermStores . length ; i < len ; i ++ ) { const pnpTermStore = pnpTermStores [ i ] ; const pnpGroup = this . _getPnPTermGroupsByNameOrId ( pnpTermStore . Id , groupNameOrID ) ; if ( ! pnpGroup ) { continue ; } const pnpTermSets = await pnpGroup . termSets . usingCaching ( ) . get ( ) ; returnTerms . push ( ... await this . _searchTermsInTermSets ( pnpTermStore , pnpTermSets , searchText , pnpGroup . Id ) ) ; } return returnTerms ; } } param searchText private async _searchAllTerms ( searchText ) { if ( Environment . type === EnvironmentType . Local ) { return SPTermStoreMockHttpClient . searchTermsByName ( searchText ) ; } await this . _ensureTermStores ( ) ; const pnpTermStores = this . _pnpTermStores ; const returnTerms = [ ] ; for ( let i = 0 , len = pnpTermStores . length ; i < len ; i ++ ) { const pnpTermStore = pnpTermStores [ i ] ; const pnpTerms = await pnpTermStore . getTerms ( { TermLabel : searchText , StringMatchOption : StringMatchOption . StartsWith , DefaultLabelOnly : true , TrimUnavailable : true , ResultCollectionSize : 30 } ) . usingCaching ( ) . get ( ) ; const batch = taxonomy . createBatch ( ) ; pnpTerms . forEach ( pnpTerm => { const pickerTerm = { key : TermStorePickerServiceHelper . cleanGuid ( pnpTerm . Id ) , name : pnpTerm . Name , path : pnpTerm . PathOfTerm , termSet : '' , termGroup : '' } ; returnTerms . push ( pickerTerm ) ; pnpTerm . termSet . group . inBatch ( batch ) . usingCaching ( ) . get ( ) . then ( pnpTermGroup => { pickerTerm . termGroup = TermStorePickerServiceHelper . cleanGuid ( pnpTermGroup . Id ) ; } ) ; pnpTerm . termSet . inBatch ( batch ) . usingCaching ( ) . get ( ) . then ( pnpTermSet => { pickerTerm . termSet = TermStorePickerServiceHelper . cleanGuid ( pnpTermSet . Id ) ; pickerTerm . termSetName = pnpTermSet . Name ; } ) ; if ( this . props . includeLabels ) { pnpTerm . labels . inBatch ( batch ) . usingCaching ( ) . get ( ) . then ( labels => { pickerTerm . labels = labels . map ( label => label . Value ) ; } ) ; } } ) ; await batch . execute ( ) ; } return returnTerms ; } param pnpTermStore param pnpTermSets param searchText param termGroupId private async _searchTermsInTermSets ( pnpTermStore , pnpTermSets , searchText , termGroupId ? ) { const returnTerms = [ ] ; const termSetGroups = { } ; const termsBatch = taxonomy . createBatch ( ) ; const labelsBatch = taxonomy . createBatch ( ) ; const lowerCasedSearchText = searchText . toLowerCase ( ) ; for ( let termSetIdx = 0 , termSetLen = pnpTermSets . length ; termSetIdx < termSetLen ; termSetIdx ++ ) { const pnpTermSet = pnpTermSets [ termSetIdx ] ; const pnpTermSetGuid = TermStorePickerServiceHelper . cleanGuid ( pnpTermSet . Id ) ; if ( ! termGroupId ) { pnpTermSet . group . inBatch ( termsBatch ) . usingCaching ( ) . get ( ) . then ( pnpTermGroup => { termSetGroups [ pnpTermSet . Id ] = pnpTermGroup . Id ; const loadedTerms = returnTerms . filter ( t => t . termSet === pnpTermSetGuid ) ; loadedTerms . forEach ( t => { t . termGroup = TermStorePickerServiceHelper . cleanGuid ( pnpTermGroup . Id ) ; } ) ; } ) ; } pnpTermSet . terms . inBatch ( termsBatch ) . usingCaching ( ) . get ( ) . then ( pnpTerms => { for ( let termIdx = 0 , termLen = pnpTerms . length ; termIdx < termLen ; termIdx ++ ) { const pnpTerm = pnpTerms [ termIdx ] ; if ( pnpTerm . Name . toLowerCase ( ) . indexOf ( lowerCasedSearchText ) === 0 ) { const pickerTerm = { key : TermStorePickerServiceHelper . cleanGuid ( pnpTerm . Id ) , name : pnpTerm . Name , path : pnpTerm . PathOfTerm , termSet : TermStorePickerServiceHelper . cleanGuid ( pnpTermSetGuid ) , termSetName : pnpTermSet . Name , termGroup : termGroupId || TermStorePickerServiceHelper . cleanGuid ( termSetGroups [ pnpTermSet . Id ] ) } ; returnTerms . push ( pickerTerm ) ; if ( this . props . includeLabels ) { pnpTerm . labels . inBatch ( labelsBatch ) . usingCaching ( ) . get ( ) . then ( pnpLabels => { pickerTerm . labels = pnpLabels . map ( l => l . Value ) ; } ) ; } } } } ) ; } await termsBatch . execute ( ) ; if ( this . props . includeLabels ) { await labelsBatch . execute ( ) ; } return returnTerms ; } /**
     * Ensures (loads if needed) term stores and term groups from taxonomy service
     */ private async _ensureTermStores ( ) { if ( ! this . _pnpTermStores ) { this . _pnpTermStores = await taxonomy . termStores . usingCaching ( ) . get ( ) ; for ( let i = 0 , len = this . _pnpTermStores . length ; i < len ; i ++ ) { const pnpTermStore = this . _pnpTermStores [ i ] ; let pnpGroups ; if ( this . props . limitByGroupNameOrID ) { const group = await this . _requestPnPTermGroupByNameOrId ( pnpTermStore , this . props . limitByGroupNameOrID ) ; pnpGroups = [ ] ; if ( group ) { pnpGroups . push ( group ) ; } } else if ( this . props . limitByTermsetNameOrID ) { const pnpTermSets = await this . _getPnPTermSetsByNameOrId ( pnpTermStore , this . props . limitByTermsetNameOrID ) ; pnpGroups = [ ] ; const groupsBatch = taxonomy . createBatch ( ) ; pnpTermSets . forEach ( pnpTermSet => { pnpTermSet . group . inBatch ( groupsBatch ) . usingCaching ( ) . get ( ) . then ( pnpGroup => { if ( ! pnpGroups . filter ( gr => gr . Id === pnpGroup . Id ) . length ) { pnpGroups . push ( pnpGroup ) ; } } ) ; } ) ; await groupsBatch . execute ( ) ; } else { pnpGroups = await pnpTermStore . groups . usingCaching ( ) . get ( ) ; } this . _pnpGroups [ pnpTermStore . Id ] = pnpGroups ; } } } param pnpTermSet pnp param groupId private _pnpTermSet2TermSet ( pnpTermSet , groupId ) { const anyPnPTermSet = pnpTermSet as any ; return { _ObjectType_ : anyPnPTermSet . _ObjectType_ , _ObjectIdentity_ : anyPnPTermSet . _ObjectIdentity_ , Id : TermStorePickerServiceHelper . cleanGuid ( pnpTermSet . Id ) , Name : pnpTermSet . Name , Description : pnpTermSet . Description , Names : pnpTermSet . Names , Group : groupId } ; } param pnpTermGroup pnp param pnpTermStore pnp private _pnpTermGroup2TermGroup ( pnpTermGroup , pnpTermStore ) { const anyPnPTermGroup = pnpTermGroup as any ; return { _ObjectType_ : anyPnPTermGroup . _ObjectType_ , _ObjectIdentity_ : anyPnPTermGroup . _ObjectIdentity_ , Id : TermStorePickerServiceHelper . cleanGuid ( pnpTermGroup . Id ) , Name : pnpTermGroup . Name , IsSystemGroup : pnpTermGroup . IsSystemGroup , TermStore : { Id : TermStorePickerServiceHelper . cleanGuid ( pnpTermStore . Id ) , Name : pnpTermStore . Name } , TermSets : { _ObjectType_ : this . _termSetCollectionObjectType , _Child_Items_ : null } } ; } param pnpTermStore pnp param termSetNameOrID private async _getPnPTermSetsByNameOrId ( pnpTermStore , termSetNameOrID ) { let pnpTermSets ; const isGuid = TermStorePickerServiceHelper . isGuid ( termSetNameOrID ) ; if ( isGuid ) { pnpTermSets = [ ] ; const pnpTermSet = await pnpTermStore . getTermSetById ( termSetNameOrID ) . usingCaching ( ) . get ( ) ; if ( pnpTermSet . Id ) { pnpTermSets . push ( pnpTermSet ) ; } } else { pnpTermSets = await pnpTermStore . getTermSetsByName ( termSetNameOrID , pnpTermStore . DefaultLanguage ) . usingCaching ( ) . get ( ) ; } return pnpTermSets ; } param termStoreId param groupNameOrID private _getPnPTermGroupsByNameOrId ( termStoreId , groupNameOrID ) { const isGuid = TermStorePickerServiceHelper . isGuid ( groupNameOrID ) ; const pnpTermStoreGroups = this . _pnpGroups [ termStoreId ] ; if ( pnpTermStoreGroups ) { const groups = pnpTermStoreGroups . filter ( pnpGroup => isGuid ? TermStorePickerServiceHelper . cleanGuid ( pnpGroup . Id ) === groupNameOrID : pnpGroup . Name === groupNameOrID ) ; if ( groups && groups . length ) { return groups [ 0 ] ; } } return null ; } param pnpTermStore pnp param groupNameOrID private async _requestPnPTermGroupByNameOrId ( pnpTermStore , groupNameOrID ) { const isGuid = TermStorePickerServiceHelper . isGuid ( groupNameOrID ) ; let group ; if ( isGuid ) { group = await pnpTermStore . getTermGroupById ( groupNameOrID ) . usingCaching ( ) . get ( ) ; } else { group = await pnpTermStore . groups . getByName ( groupNameOrID ) . usingCaching ( ) . get ( ) ; } if ( group . Id ) { return group ; } return null ; } }