11import { HDSLibError } from '../errors.js' ;
2+ import { getModel } from '../HDSModel/HDSModelInitAndSingleton.js' ;
23import { validateLocalizableText } from '../localizeText.js' ;
3- import type { localizableText } from '../../types/localizeText.d.ts' ;
4+ import type { localizableText , localizableTextLanguages } from '../../types/localizeText.d.ts' ;
5+
46
57declare type PermissionItem = { streamId : string , defaultName : string , level : string } ;
68
9+ const CURRENT_VERSION = 1 ;
10+
711/**
812 * Each Collector has one Request
913 * Which contains
@@ -22,13 +26,15 @@ export class CollectorRequest {
2226 #requester: { name : string } ;
2327 #app: { id : string , url : string | null , data : any } ;
2428 #permissions: Array < PermissionItem > ;
29+ #sections: Array < CollectorRequestSection > ;
2530
2631 #extraContent: any ;
2732 constructor ( content : any ) {
28- this . #version = 0 ;
33+ this . #version = CURRENT_VERSION ;
2934 this . #requester = { name : null } ;
3035 this . #app = { id : null , url : null , data : { } } ;
3136 this . #permissions = [ ] ;
37+ this . #sections = [ ] ;
3238 this . setContent ( content ) ;
3339 }
3440
@@ -51,32 +57,53 @@ export class CollectorRequest {
5157 */
5258 setContent ( content : any ) {
5359 const futureContent = structuredClone ( content ) ;
60+
5461 // validate content
55- if ( futureContent . version ) {
62+ if ( futureContent . version != null ) {
5663 const numV = Number . parseInt ( futureContent . version ) ;
57- if ( numV !== this . #version) throw new HDSLibError ( `Invalid CollectorRequest content version: ${ futureContent . version } ` ) ;
64+ if ( numV === 0 ) {
65+ vo0ToV1 ( futureContent ) ; // convert to v1 if needed
66+ } else {
67+ if ( numV !== this . #version) throw new HDSLibError ( `Invalid CollectorRequest content version: ${ futureContent . version } ` ) ;
68+ }
5869 delete futureContent . version ;
5970 }
6071
72+ // -- title, consent, description
6173 for ( const key of [ 'title' , 'consent' , 'description' ] ) {
6274 if ( futureContent [ key ] != null ) {
6375 this [ key ] = futureContent [ key ] ;
6476 }
6577 delete futureContent [ key ] ;
6678 }
79+
80+ // -- requester
6781 if ( futureContent . requester ) {
6882 if ( futureContent . requester . name != null ) {
6983 this . requesterName = futureContent . requester . name ;
7084 }
7185 delete futureContent . requester ;
7286 }
87+
88+ // -- app
7389 if ( futureContent . app ) {
7490 if ( futureContent . app . id != null ) { this . appId = futureContent . app . id ; }
7591 if ( futureContent . app . url != null ) { this . appUrl = futureContent . app . url ; }
7692 if ( futureContent . app . data != null ) { this . appCustomData = futureContent . app . data ; }
7793 delete futureContent . app ;
7894 }
7995
96+ // -- sections
97+ if ( futureContent . sections != null ) {
98+ for ( const sectionData of futureContent . sections ) {
99+ const section = new CollectorRequestSection ( sectionData . key , sectionData . type ) ;
100+ section . setName ( sectionData . name ) ;
101+ section . addItemKeys ( sectionData . itemKeys ) ;
102+ this . #sections. push ( section ) ;
103+ }
104+ }
105+
106+ // -- permissions
80107 if ( futureContent . permissions ) {
81108 this . #permissions = [ ] ; // reset permissions
82109 futureContent . permissions . forEach ( ( p : PermissionItem ) => {
@@ -116,6 +143,14 @@ export class CollectorRequest {
116143
117144 get permissions ( ) { return this . #permissions; }
118145
146+ get sectionsData ( ) {
147+ const result = [ ] ;
148+ for ( const section of this . #sections) {
149+ result . push ( section . getData ( ) ) ;
150+ }
151+ return result ;
152+ }
153+
119154 // ---------- permissions ---------- //
120155 addPermissions ( streamId : string , defaultName : string , level : string ) {
121156 this . #permissions. push ( { streamId, defaultName, level} ) ;
@@ -126,6 +161,7 @@ export class CollectorRequest {
126161 */
127162 get content ( ) {
128163 const content = {
164+ version : this . version ,
129165 title : this . title ,
130166 consent : this . consent ,
131167 description : this . description ,
@@ -137,7 +173,8 @@ export class CollectorRequest {
137173 id : this . appId ,
138174 url : this . appUrl ,
139175 data : this . appCustomData
140- }
176+ } ,
177+ sections : this . sectionsData
141178 } ;
142179 Object . assign ( content , this . #extraContent) ;
143180 return content ;
@@ -149,4 +186,78 @@ function validateString (key, totest) {
149186 return totest ;
150187}
151188
189+ const RequestSectionType = {
190+ recurring : 'recurring' ,
191+ permanent : 'permanent'
192+ }
193+ type RequestSectionType = ( typeof RequestSectionType ) [ keyof typeof RequestSectionType ] ;
194+
195+ class CollectorRequestSection {
196+ #type: RequestSectionType ;
197+ #name: localizableText ;
198+ #key: string ;
199+ #itemKeys: Array < string > ;
200+
201+ constructor ( key : string , type : RequestSectionType ) {
202+ this . #key = key ;
203+ this . #type = type ;
204+ this . #itemKeys = [ ] ;
205+ this . #name = {
206+ en : ''
207+ }
208+ }
209+
210+ addItemKeys ( keys : Array < string > ) {
211+ keys . forEach ( ( k ) => this . addItemKey ( k ) ) ;
212+ }
213+
214+ addItemKey ( key : string ) {
215+ getModel ( ) . itemsDefs . forKey ( key ) ; // will throw error if not found
216+ if ( this . #itemKeys. includes ( key ) ) return ; // avoid double entries
217+ this . #itemKeys. push ( key ) ;
218+ }
219+
220+ setName ( localizedName : localizableText ) {
221+ for ( const [ languageCode , name ] of Object . entries ( localizedName ) ) {
222+ this . setNameLocal ( languageCode as localizableTextLanguages , name ) ;
223+ }
224+ }
225+
226+ setNameLocal ( languageCode : localizableTextLanguages , name : string ) {
227+ this . #name[ languageCode ] = name ;
228+ }
152229
230+ get type ( ) { return this . #type }
231+ get key ( ) { return this . #key }
232+ get itemKeys ( ) { return this . #itemKeys }
233+
234+ getData ( ) {
235+ return {
236+ key : this . key ,
237+ type : this . #type,
238+ name : this . #name,
239+ itemKeys : this . #itemKeys
240+ }
241+ }
242+
243+ }
244+
245+ /**
246+ * Transform data to match v1
247+ * @param v0Data
248+ */
249+ function vo0ToV1 ( v0Data : any ) {
250+ if ( v0Data . app ?. data ?. forms ) {
251+ if ( v0Data . sections ) throw new HDSLibError ( 'Cannot mix data.forms & sections' , v0Data ) ;
252+ v0Data . sections = [ ] ;
253+ for ( const [ key , value ] of Object . entries ( v0Data . app . data . forms ) as [ key : string , value : any ] ) {
254+ value . key = key ;
255+ value . name = {
256+ en : value . name
257+ }
258+ v0Data . sections . push ( value )
259+ }
260+ delete v0Data . app . data . forms ;
261+ }
262+ v0Data . version = 1 ;
263+ }
0 commit comments