import type { IComponentOptions , IController , IInterpolateService , ISCEService } from 'angular' ; import { module } from 'angular' ; import DOMPurify from 'dompurify' ; import { get , partition } from 'lodash' ; interface IAnnotationsMap { [ key ] : string ; } interface ICustomSection { title : string ; key : string ; isHtml : boolean ; content : string ; } interface ICustomSectionMap { [ key ] : ICustomSection [ ] ; } class KubernetesAnnotationCustomSections implements IController { private resource : any ; public manifest : any ; public customSections : ICustomSectionMap ; public static $inject = [ '$sce' , '$interpolate' ] ; constructor ( private $sce , private $interpolate ) { } public $onInit ( ) { const annotations = get ( this , [ 'manifest' , 'metadata' , 'annotations' ] ) ; if ( annotations == null ) { return ; } this . populateCustomSections ( annotations ) ; } private populateCustomSections ( annotations ) { const customSections = Object . keys ( annotations ) . reduce ( ( memo , annotationKey ) => { const entry = this . annotationToEntry ( annotations [ annotationKey ] , annotationKey ) ; if ( entry != null && entry . title ) { memo [ entry . title ] = memo [ entry . title ] || [ ] ; memo [ entry . title ] . push ( entry ) ; } return memo ; } , { } ) ; this . customSections = Object . keys ( customSections ) . reduce ( ( memo , sectionTitle ) => { const entriesHtmlText = partition ( customSections [ sectionTitle ] , ( section ) => section . isHtml ) ; memo [ sectionTitle ] = entriesHtmlText [ 1 ] . concat ( entriesHtmlText [ 0 ] ) ; return memo ; } , { } ) ; } private annotationToEntry ( content , annotationKey ) { const parsed = this . parseAnnotationKey ( annotationKey ) ; if ( parsed == null ) { return null ; } if ( this . resource && content . includes ( '{{' ) ) { content = this . $interpolate ( content ) ( { ... this . resource , manifest : this . manifest } ) ; } return { title : parsed . title . replace ( /-/g , ' ' ) . trim ( ) , key : parsed . key . replace ( /-/g , ' ' ) . trim ( ) , content : parsed . isHtml ? this . sanitizeContent ( content ) : content , isHtml : parsed . isHtml , } ; } private parseAnnotationKey ( annotationKey ) { const keyParts = /([^.]+)\.details\.(html\.)?spinnaker\.io(?:\/(.*))?/ . exec ( annotationKey ) ; if ( keyParts == null || keyParts . length !== 4 ) { return null ; } return { title : keyParts [ 1 ] || '' , key : keyParts [ 3 ] || '' , isHtml : ! ! keyParts [ 2 ] , } ; } private sanitizeContent ( unsanitized ) { const sanitized = DOMPurify . sanitize ( unsanitized , { ADD_ATTR : [ 'target' ] , } ) ; return this . $sce . trustAsHtml ( sanitized ) ; } } const kubernetesAnnotationCustomSectionsComponent = { bindings : { manifest : '<' , resource : '<' } , controller : [ '$sce' , '$interpolate' , KubernetesAnnotationCustomSections ] , controllerAs : 'ctrl' , template : `
    <collapsible-section expanded="true" ng-if="ctrl.manifest" ng-repeat="(section, entries) in ctrl.customSections" heading="{{ section }}">
      <div ng-repeat="entry in entries">
        <div ng-if="entry.isHtml" ng-bind-html="entry.content"></div>
        <div ng-if="!entry.isHtml">
          <span ng-if="entry.key" style="font-weight:bold">{{ entry.key }}</span>
          <span>{{ entry.content }}</span>
        </div>
      </div>
    </collapsible-section>
  ` , } ; export const KUBERNETES_ANNOTATION_CUSTOM_SECTIONS = 'spinnaker.kubernetes.v2.manifest.annotation.custom.sections' ; module ( KUBERNETES_ANNOTATION_CUSTOM_SECTIONS , [ ] ) . component ( 'kubernetesAnnotationCustomSections' , kubernetesAnnotationCustomSectionsComponent ) ;