@ikonintegration/module-config-api 2.0.19 → 2.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Api.ts CHANGED
@@ -2934,7 +2934,7 @@ export interface FaqResponse {
2934
2934
  answer?: string
2935
2935
  }
2936
2936
 
2937
- export interface InputVnr9Fm759Zi {
2937
+ export interface InputAcavfff528 {
2938
2938
  /**
2939
2939
  * Tenant ID this FAQ belongs to
2940
2940
  * @example "tenant456"
@@ -2957,7 +2957,7 @@ export interface InputVnr9Fm759Zi {
2957
2957
  answer?: string
2958
2958
  }
2959
2959
 
2960
- export interface InputGbe7Fhz907 {
2960
+ export interface InputUshnduveu4 {
2961
2961
  /**
2962
2962
  * Tenant ID this FAQ belongs to
2963
2963
  * @example "tenant456"
@@ -3128,6 +3128,19 @@ export interface CertificationResponse {
3128
3128
  * @example "req123"
3129
3129
  */
3130
3130
  validityRequirementID?: string
3131
+ /** Overrides email templates for specific email events */
3132
+ emailEventOverrides?: {
3133
+ /**
3134
+ * Email event identifier
3135
+ * @example "event001"
3136
+ */
3137
+ eventID: string
3138
+ /**
3139
+ * Email template identifier override for the event
3140
+ * @example "template001"
3141
+ */
3142
+ templateID: string
3143
+ }[]
3131
3144
  /** Warning threshold for incomplete certification */
3132
3145
  warningIncompleteCertificationDays?: {
3133
3146
  /**
@@ -3328,6 +3341,19 @@ export interface CertificationRequestBody {
3328
3341
  * @example "req123"
3329
3342
  */
3330
3343
  validityRequirementID?: string
3344
+ /** Overrides email templates for specific email events */
3345
+ emailEventOverrides?: {
3346
+ /**
3347
+ * Email event identifier
3348
+ * @example "event001"
3349
+ */
3350
+ eventID: string
3351
+ /**
3352
+ * Email template identifier override for the event
3353
+ * @example "template001"
3354
+ */
3355
+ templateID: string
3356
+ }[]
3331
3357
  /** Warning threshold for incomplete certification */
3332
3358
  warningIncompleteCertificationDays?: {
3333
3359
  /**
@@ -3549,6 +3575,19 @@ export interface CertificationListResponse {
3549
3575
  * @example "req123"
3550
3576
  */
3551
3577
  validityRequirementID?: string
3578
+ /** Overrides email templates for specific email events */
3579
+ emailEventOverrides?: {
3580
+ /**
3581
+ * Email event identifier
3582
+ * @example "event001"
3583
+ */
3584
+ eventID: string
3585
+ /**
3586
+ * Email template identifier override for the event
3587
+ * @example "template001"
3588
+ */
3589
+ templateID: string
3590
+ }[]
3552
3591
  /** Warning threshold for incomplete certification */
3553
3592
  warningIncompleteCertificationDays?: {
3554
3593
  /**
@@ -3922,6 +3961,19 @@ export interface CourseResponse {
3922
3961
  */
3923
3962
  courseFailed?: string | null
3924
3963
  }
3964
+ /** Overrides email templates for specific email events */
3965
+ emailEventOverrides?: {
3966
+ /**
3967
+ * Email event identifier
3968
+ * @example "event001"
3969
+ */
3970
+ eventID: string
3971
+ /**
3972
+ * Email template identifier override for the event
3973
+ * @example "template001"
3974
+ */
3975
+ templateID: string
3976
+ }[]
3925
3977
  /**
3926
3978
  * Display name
3927
3979
  * @example "Safety Training Course"
@@ -4226,6 +4278,19 @@ export interface CourseRequestBody {
4226
4278
  */
4227
4279
  courseFailed?: string | null
4228
4280
  }
4281
+ /** Overrides email templates for specific email events */
4282
+ emailEventOverrides?: {
4283
+ /**
4284
+ * Email event identifier
4285
+ * @example "event001"
4286
+ */
4287
+ eventID: string
4288
+ /**
4289
+ * Email template identifier override for the event
4290
+ * @example "template001"
4291
+ */
4292
+ templateID: string
4293
+ }[]
4229
4294
  /**
4230
4295
  * Display name
4231
4296
  * @example "Safety Training Course"
@@ -4592,6 +4657,19 @@ export interface CourseListResponse {
4592
4657
  */
4593
4658
  courseFailed?: string | null
4594
4659
  }
4660
+ /** Overrides email templates for specific email events */
4661
+ emailEventOverrides?: {
4662
+ /**
4663
+ * Email event identifier
4664
+ * @example "event001"
4665
+ */
4666
+ eventID: string
4667
+ /**
4668
+ * Email template identifier override for the event
4669
+ * @example "template001"
4670
+ */
4671
+ templateID: string
4672
+ }[]
4595
4673
  /**
4596
4674
  * Display name
4597
4675
  * @example "Safety Training Course"
@@ -5354,7 +5432,7 @@ export class HttpClient<SecurityDataType = unknown> {
5354
5432
 
5355
5433
  /**
5356
5434
  * @title No title
5357
- * @version 2.0.19
5435
+ * @version 2.0.21
5358
5436
  * @baseUrl http://localhost:8080
5359
5437
  * @contact Ikon Integration <ikon@ikonintegration.com> (http://example.com)
5360
5438
  */
@@ -6019,7 +6097,7 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa
6019
6097
  * @request POST:/tenant/{tenantID}/faq/{faqID}
6020
6098
  * @secure
6021
6099
  */
6022
- createFaq: (tenantId: string, faqId: string, data: InputVnr9Fm759Zi, params: RequestParams = {}) =>
6100
+ createFaq: (tenantId: string, faqId: string, data: InputAcavfff528, params: RequestParams = {}) =>
6023
6101
  this.request<CreateFaqData, CreateFaqError>({
6024
6102
  path: `/tenant/${tenantId}/faq/${faqId}`,
6025
6103
  method: 'POST',
@@ -6038,7 +6116,7 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa
6038
6116
  * @request PUT:/tenant/{tenantID}/faq/{faqID}
6039
6117
  * @secure
6040
6118
  */
6041
- updateFaq: (tenantId: string, faqId: string, data: InputGbe7Fhz907, params: RequestParams = {}) =>
6119
+ updateFaq: (tenantId: string, faqId: string, data: InputUshnduveu4, params: RequestParams = {}) =>
6042
6120
  this.request<UpdateFaqData, UpdateFaqError>({
6043
6121
  path: `/tenant/${tenantId}/faq/${faqId}`,
6044
6122
  method: 'PUT',
package/CacheClient.ts CHANGED
@@ -1,21 +1,26 @@
1
1
  // @ts-nocheck — This file is compiled in CI alongside generated sources (Api.js)
2
- import { Api } from './Api.js'
3
- import type { CustomerResponse, TenantResponse } from './Api.js'
4
2
  import { MemoryCache } from './MemoryCache.js'
5
- import { RedisLike, CacheConfig, CacheConstants } from './CacheTypes.js'
3
+ import {
4
+ RedisLike,
5
+ CacheConfig,
6
+ CacheConstants,
7
+ CachedCustomer,
8
+ CachedTenant,
9
+ CachedCertification,
10
+ CachedCourse,
11
+ } from './CacheTypes.js'
6
12
 
7
13
  /**
8
14
  * Extended client that wraps the auto-generated Api with Redis cache support.
9
- * Provides direct Redis-backed lookups for customers, tenants, certifications, and courses.
15
+ * Provides direct Redis-backed lookups for expanded customers, tenants, certifications, and courses
16
+ * as they are stored in the persistent cache (not the raw API responses).
10
17
  *
11
18
  * Usage:
12
19
  * ```ts
13
- * import { CacheClient } from "@ikonintegration/module-config-api/cache";
20
+ * import { CacheClient } from "@ikonintegration/module-config-api";
14
21
  *
15
22
  * const client = new CacheClient({
16
- * // API layer (passed to the generated Api client)
17
23
  * baseUrl: "https://api.example.com",
18
- * // Cache layer (optional — enables Redis-backed lookups)
19
24
  * cache: {
20
25
  * endpoint: "redis.example.com",
21
26
  * username: "default",
@@ -27,28 +32,21 @@ import { RedisLike, CacheConfig, CacheConstants } from './CacheTypes.js'
27
32
  * ```
28
33
  */
29
34
  export class CacheClient {
30
- public api: Api<unknown>
31
35
  private _localCache: MemoryCache
36
+
32
37
  private _remoteCache: RedisLike | null = null
38
+
33
39
  private _cacheConfig: CacheConfig | null
34
40
 
35
- constructor(options: {
36
- baseUrl?: string
37
- baseApiParams?: ConstructorParameters<typeof Api>[0]
38
- cache?: CacheConfig
39
- }) {
40
- this.api = new Api({
41
- baseURL: options.baseUrl,
42
- ...options.baseApiParams,
43
- })
41
+ constructor(options: { cache?: CacheConfig }) {
44
42
  this._localCache = new MemoryCache()
45
43
  this._cacheConfig = options.cache || null
46
44
  }
47
45
 
48
46
  /********* CACHE LAYER — Customers *********/
49
47
 
50
- async getAllCustomers(): Promise<CustomerResponse[] | null> {
51
- let allCustomers = this._localCache.getValue<CustomerResponse[]>(CacheConstants.CustomerPrefix)
48
+ async getAllCustomers(): Promise<CachedCustomer[] | null> {
49
+ let allCustomers = this._localCache.getValue<CachedCustomer[]>(CacheConstants.CustomerPrefix)
52
50
  if (!allCustomers) {
53
51
  console.debug('ConfigClient - Customers Fetch All')
54
52
  allCustomers = await this._getAllRemoteCustomers()
@@ -58,12 +56,12 @@ export class CacheClient {
58
56
  return allCustomers
59
57
  }
60
58
 
61
- async getCustomerByID(customerID: string): Promise<CustomerResponse | null> {
59
+ async getCustomerByID(customerID: string): Promise<CachedCustomer | null> {
62
60
  // Attempt local cache
63
- let customer = this._localCache.getValue<CustomerResponse>(`${CacheConstants.CustomerPrefix}${customerID}`)
61
+ let customer = this._localCache.getValue<CachedCustomer>(`${CacheConstants.CustomerPrefix}${customerID}`)
64
62
  // Attempt from allCustomers local cache
65
63
  if (!customer) {
66
- const allCustomers = this._localCache.getValue<CustomerResponse[]>(CacheConstants.CustomerPrefix)
64
+ const allCustomers = this._localCache.getValue<CachedCustomer[]>(CacheConstants.CustomerPrefix)
67
65
  customer = allCustomers?.find(c => c.id === customerID) || null
68
66
  }
69
67
  // Attempt remote cache
@@ -80,8 +78,8 @@ export class CacheClient {
80
78
 
81
79
  /********* CACHE LAYER — Tenants *********/
82
80
 
83
- async getAllTenants(): Promise<TenantResponse[] | null> {
84
- let allTenants = this._localCache.getValue<TenantResponse[]>(CacheConstants.TenantPrefix)
81
+ async getAllTenants(): Promise<CachedTenant[] | null> {
82
+ let allTenants = this._localCache.getValue<CachedTenant[]>(CacheConstants.TenantPrefix)
85
83
  if (!allTenants) {
86
84
  console.debug('ConfigClient - Tenants Fetch All')
87
85
  allTenants = await this._getAllRemoteTenants()
@@ -91,8 +89,8 @@ export class CacheClient {
91
89
  return allTenants
92
90
  }
93
91
 
94
- async getTenantByID(tenantID: string): Promise<TenantResponse | null> {
95
- let tenant = this._localCache.getValue<TenantResponse>(`${CacheConstants.TenantPrefix}${tenantID}`)
92
+ async getTenantByID(tenantID: string): Promise<CachedTenant | null> {
93
+ let tenant = this._localCache.getValue<CachedTenant>(`${CacheConstants.TenantPrefix}${tenantID}`)
96
94
  if (!tenant) {
97
95
  console.debug('ConfigClient - Tenant Fetch:', tenantID)
98
96
  await this._initCacheClient()
@@ -106,19 +104,19 @@ export class CacheClient {
106
104
 
107
105
  /********* CACHE LAYER — Certifications & Courses *********/
108
106
 
109
- async getCertificationByID(certID: string, tenantID: string) {
107
+ async getCertificationByID(certID: string, tenantID: string): Promise<CachedCertification | null> {
110
108
  const tenant = await this.getTenantByID(tenantID)
111
- if (!tenant || !Array.isArray((tenant as any).certifications)) return null
112
- return (tenant as any).certifications.find((c: any) => c.id === certID) || null
109
+ if (!tenant?.certifications) return null
110
+ return tenant.certifications.find(c => c.id === certID) || null
113
111
  }
114
112
 
115
- async getCourseByID(courseID: string, tenantID: string) {
113
+ async getCourseByID(courseID: string, tenantID: string): Promise<CachedCourse | null> {
116
114
  const tenant = await this.getTenantByID(tenantID)
117
- if (!tenant || !Array.isArray((tenant as any).certifications)) return null
118
- for (const cert of (tenant as any).certifications) {
115
+ if (!tenant?.certifications) return null
116
+ for (const cert of tenant.certifications) {
119
117
  for (const req of cert.requirements || []) {
120
118
  if (Array.isArray(req)) {
121
- const found = req.find((r: any) => r.id === courseID)
119
+ const found = req.find(r => r.id === courseID)
122
120
  if (found) return found
123
121
  } else if (req.id === courseID) return req
124
122
  }
@@ -126,25 +124,29 @@ export class CacheClient {
126
124
  return null
127
125
  }
128
126
 
129
- async getCourseInCertificationByID(certificationID: string, courseID: string, tenantID: string) {
127
+ async getCourseInCertificationByID(
128
+ certificationID: string,
129
+ courseID: string,
130
+ tenantID: string
131
+ ): Promise<CachedCourse | null> {
130
132
  const tenant = await this.getTenantByID(tenantID)
131
- if (!tenant || !Array.isArray((tenant as any).certifications)) return null
132
- const cert = (tenant as any).certifications.find((c: any) => c.id === certificationID)
133
+ if (!tenant?.certifications) return null
134
+ const cert = tenant.certifications.find(c => c.id === certificationID)
133
135
  if (!cert) return null
134
136
  for (const req of cert.requirements || []) {
135
137
  if (Array.isArray(req)) {
136
- const found = req.find((r: any) => r.id === courseID)
138
+ const found = req.find(r => r.id === courseID)
137
139
  if (found) return found
138
140
  } else if (req.id === courseID) return req
139
141
  }
140
142
  return null
141
143
  }
142
144
 
143
- async getAllCoursesByTenantID(tenantID: string) {
145
+ async getAllCoursesByTenantID(tenantID: string): Promise<CachedCourse[] | null> {
144
146
  const tenant = await this.getTenantByID(tenantID)
145
- if (!tenant || !Array.isArray((tenant as any).certifications)) return null
146
- const courses: any[] = []
147
- for (const cert of (tenant as any).certifications) {
147
+ if (!tenant?.certifications) return null
148
+ const courses: CachedCourse[] = []
149
+ for (const cert of tenant.certifications) {
148
150
  for (const req of cert.requirements || []) {
149
151
  if (Array.isArray(req)) {
150
152
  for (const sub of req) courses.push(sub)
@@ -154,11 +156,11 @@ export class CacheClient {
154
156
  return courses
155
157
  }
156
158
 
157
- async getAllCoursesByCertificationID(certID: string, tenantID: string) {
159
+ async getAllCoursesByCertificationID(certID: string, tenantID: string): Promise<CachedCourse[] | null> {
158
160
  const tenant = await this.getTenantByID(tenantID)
159
- if (!tenant || !Array.isArray((tenant as any).certifications)) return null
160
- const courses: any[] = []
161
- for (const cert of (tenant as any).certifications) {
161
+ if (!tenant?.certifications) return null
162
+ const courses: CachedCourse[] = []
163
+ for (const cert of tenant.certifications) {
162
164
  if (cert.id !== certID) continue
163
165
  for (const req of cert.requirements || []) {
164
166
  if (Array.isArray(req)) {
@@ -169,18 +171,18 @@ export class CacheClient {
169
171
  return courses
170
172
  }
171
173
 
172
- async getAllCourses() {
174
+ async getAllCourses(): Promise<CachedCourse[]> {
173
175
  return this.getAllCoursesByCustomerID(null)
174
176
  }
175
177
 
176
- async getAllCoursesByCustomerID(customerID: string | null) {
178
+ async getAllCoursesByCustomerID(customerID: string | null): Promise<CachedCourse[]> {
177
179
  const allTenants = await this.getAllTenants()
178
180
  if (!allTenants) return []
179
- const filtered = allTenants.filter(t => !customerID || (t as any).customerID === customerID)
180
- const courses: any[] = []
181
+ const filtered = allTenants.filter(t => !customerID || t.customerID === customerID)
182
+ const courses: CachedCourse[] = []
181
183
  for (const tenant of filtered) {
182
- if (!Array.isArray((tenant as any).certifications)) continue
183
- for (const cert of (tenant as any).certifications) {
184
+ if (!tenant.certifications) continue
185
+ for (const cert of tenant.certifications) {
184
186
  for (const req of cert.requirements || []) {
185
187
  if (Array.isArray(req)) {
186
188
  for (const sub of req) {
@@ -199,13 +201,13 @@ export class CacheClient {
199
201
 
200
202
  async getCourseProviderByIDs(courseProviderID: string, customerID: string) {
201
203
  const customer = await this.getCustomerByID(customerID)
202
- if (!customer || !Array.isArray((customer as any).courseProviders)) return null
203
- return (customer as any).courseProviders.find((p: any) => p === courseProviderID) || null
204
+ if (!customer?.allowedOnlineCourseProviders) return null
205
+ return customer.allowedOnlineCourseProviders.find(p => p.id === courseProviderID) || null
204
206
  }
205
207
 
206
208
  /********* Private *********/
207
209
 
208
- private async _getAllRemoteCustomers(): Promise<CustomerResponse[] | null> {
210
+ private async _getAllRemoteCustomers(): Promise<CachedCustomer[] | null> {
209
211
  await this._initCacheClient()
210
212
  if (!this._remoteCache) return null
211
213
  const allKeys = await this._remoteCache.keys(`${CacheConstants.CustomerPrefix}*`)
@@ -214,7 +216,7 @@ export class CacheClient {
214
216
  return Array.isArray(raw) ? raw.map(r => JSON.parse(r)) : null
215
217
  }
216
218
 
217
- private async _getAllRemoteTenants(): Promise<TenantResponse[] | null> {
219
+ private async _getAllRemoteTenants(): Promise<CachedTenant[] | null> {
218
220
  await this._initCacheClient()
219
221
  if (!this._remoteCache) return null
220
222
  const allKeys = await this._remoteCache.keys(`${CacheConstants.TenantPrefix}*`)
package/CacheTypes.ts CHANGED
@@ -21,7 +21,7 @@ export interface CacheConfig {
21
21
  }
22
22
 
23
23
  /**
24
- * Cache key prefixes and TTL constants (matching the legacy client).
24
+ * Cache key prefixes and TTL constants (matching the persistent cache).
25
25
  */
26
26
  export const CacheConstants = {
27
27
  // Customer
@@ -33,3 +33,273 @@ export const CacheConstants = {
33
33
  AllTenantsTTL: 60 * 60, // 1 hour
34
34
  TenantTTL: 60, // 1 minute
35
35
  } as const
36
+
37
+ // ─── Expanded (Cached) Types ────────────────────────────────────────────────
38
+
39
+ /**
40
+ * Course provider object as stored in the expanded customer cache.
41
+ */
42
+ export interface CachedCourseProvider {
43
+ id: string
44
+ type?: string
45
+ onlineTemplateIDs?: string[]
46
+ provider?: any
47
+ createdBy?: string
48
+ createdOn?: number
49
+ updatedBy?: string
50
+ updatedOn?: number
51
+ }
52
+
53
+ /**
54
+ * Tile permissions as injected into the cached customer.
55
+ */
56
+ export interface CachedTilePermissions {
57
+ customerID?: string
58
+ tilePermissions?: Record<string, any>
59
+ [key: string]: any
60
+ }
61
+
62
+ /**
63
+ * The expanded customer object stored in the persistent cache.
64
+ * Differs from CustomerResponse:
65
+ * - `allowedOnlineCourseProviders` contains full CourseProvider objects (not just IDs)
66
+ * - `tilePermissions` is injected from the PortalTilePermissions table
67
+ */
68
+ export interface CachedCustomer {
69
+ id: string
70
+ createdBy?: string
71
+ createdOn?: number
72
+ updatedBy?: string
73
+ updatedOn?: number
74
+ timezoneCode?: string
75
+ adminRole?: string
76
+ userRole?: string
77
+ instructorRole?: string
78
+ idmKey?: string
79
+ idmToken?: string
80
+ idmAPIUser?: string
81
+ idmAPIPassword?: string
82
+ mailerSender?: string
83
+ preventUserFromUpdatingNames?: boolean
84
+ orgModEndpoint?: string
85
+ orgAdminRole?: string
86
+ orgSubAdminRole?: string
87
+ orgUserRole?: string
88
+ orgModNamespace?: string
89
+ /** Expanded: full CourseProvider objects (not just IDs) */
90
+ allowedOnlineCourseProviders?: CachedCourseProvider[]
91
+ allowedForeignReqs?: string[]
92
+ displayName?: string
93
+ sharedModuleLicenseAPIKey?: string
94
+ sharedModuleConfigAPIKey?: string
95
+ sharedModuleAuditingAPIKey?: string
96
+ sharedModuleAgreementAPIKey?: string
97
+ sharedModuleOrganizationAPIKey?: string
98
+ sharedModuleClassroomAPIKey?: string
99
+ sharedModuleMailerAPIKey?: string
100
+ authConfig?: Record<string, any>
101
+ portalConfig?: {
102
+ theme?: string
103
+ domain?: string
104
+ cookiesHubDomain?: string
105
+ acceptConnectionsFrom?: string
106
+ }
107
+ theme?: Record<string, any>
108
+ domain?: string
109
+ cookiesHubDomain?: string
110
+ acceptConnectionsFrom?: string[]
111
+ worksafeTerm?: string
112
+ cancelationPolicies?: Array<{
113
+ id?: string
114
+ description?: string
115
+ cancelPolicy?: {
116
+ isBusinessDays?: boolean
117
+ canBeCanceled?: boolean
118
+ canBeRescheduled?: boolean
119
+ rules?: Array<{
120
+ untilDaysBefore?: number
121
+ percentageRefunded?: number
122
+ amountCharged?: number
123
+ deductEnrolmentPayment?: boolean
124
+ }>
125
+ }
126
+ }>
127
+ salesTaxLabel?: string
128
+ ignoreCustomAttributesForSignUp?: boolean
129
+ timezone?: string
130
+ /** Injected from PortalTilePermissions table */
131
+ tilePermissions?: CachedTilePermissions | null
132
+ cooldownPolicies?: Array<{
133
+ id?: string
134
+ description?: string
135
+ cooldownPolicy?: {
136
+ rules?: Array<{
137
+ untilFailedAttempts?: number
138
+ cooldownPeriod?: number
139
+ invalidateCourseRequirements?: boolean
140
+ lockApplication?: boolean
141
+ blockCourse?: boolean
142
+ autoUnlockInDays?: number
143
+ }>
144
+ }
145
+ }>
146
+ ui?: {
147
+ banner?: {
148
+ type?: string
149
+ content?: string
150
+ }
151
+ }
152
+ featureFlags?: Array<{
153
+ name: string
154
+ enabled: boolean
155
+ metadata?: string
156
+ }>
157
+ [key: string]: any
158
+ }
159
+
160
+ /**
161
+ * Course object as stored inside cached certification requirements.
162
+ */
163
+ export interface CachedCourse {
164
+ id: string
165
+ certificationID: string
166
+ tenantID?: string
167
+ customerID?: string
168
+ productID?: string
169
+ waivePayments?: boolean
170
+ coolDownPeriod?: number | number[] | null
171
+ coolDownPolicy?: any
172
+ enableRequestToEnrol?: boolean
173
+ materialsProductID?: string
174
+ maxRetry?: number
175
+ requirementsIDs?: string[]
176
+ requiredOnRenew?: boolean
177
+ validityPeriod?: number
178
+ validityPeriodInMonths?: number
179
+ validityPeriodInYears?: number
180
+ webhooks?: string[]
181
+ isRetakeAllowed?: boolean
182
+ allowStudentUploads?: boolean
183
+ allowIfAnyRequirementMet?: boolean
184
+ allowOnlyIfCertificationIsValid?: boolean
185
+ gracePeriodAfterCertCompletion?: number
186
+ courseFailWebhooks?: string[]
187
+ cooldownStartWebhooks?: string[]
188
+ cooldownEndWebhooks?: string[]
189
+ lockAfterFailure?: boolean
190
+ agreementID?: string
191
+ isMandatory?: boolean
192
+ points?: number
193
+ courseBooks?: Array<{ fileID: string; fileName: string; attachToEmail?: boolean }>
194
+ courseCertificateType?: string
195
+ courseCertificateTemplate?: string
196
+ courseCertNumberMaskFormat?: string
197
+ courseCertNumberMode?: string
198
+ courseCertNumberInitialNumber?: string
199
+ externalRequirementWebhook?: string
200
+ reuseCertNumber?: boolean
201
+ warningDuePaymentRemainingDays?: number
202
+ warningRemainingDays?: Array<{ template: string; period: number }>
203
+ onlineWarningRemainingDays?: Array<{ template: string; period: number }>
204
+ emailComplement?: string
205
+ emailTemplates?: { coursePassed?: string | null; courseFailed?: string | null }
206
+ emailEventOverrides?: Array<{ eventID: string; templateID: string }>
207
+ displayName?: string
208
+ cardDescription?: string
209
+ description?: string
210
+ uiTerm?: string
211
+ uiLogo?: string
212
+ calendarColor?: string
213
+ showCollapsed?: boolean
214
+ passUiTerm?: string
215
+ failUiTerm?: string
216
+ courseType?: string
217
+ preventLaunchIfCompleted?: boolean
218
+ requirementUserAttribIDs?: Array<{ partition: string; attributeName: string; description: string }>
219
+ createdBy?: string
220
+ createdOn?: number
221
+ updatedBy?: string
222
+ updatedOn?: number
223
+ [key: string]: any
224
+ }
225
+
226
+ /**
227
+ * Training provider object as stored inside cached certifications.
228
+ */
229
+ export interface CachedTrainingProvider {
230
+ id: string
231
+ customerID: string
232
+ name?: string
233
+ url?: string
234
+ isDisabled?: boolean
235
+ createdBy?: string
236
+ createdOn?: number
237
+ updatedBy?: string
238
+ updatedOn?: number
239
+ }
240
+
241
+ /**
242
+ * Application section within a cached certification.
243
+ */
244
+ export interface CachedApplicationSection {
245
+ applicationEnabled?: boolean
246
+ equivalenciesSection?: {
247
+ sectionEnabled?: boolean
248
+ /** Expanded equivalence objects with isDisabled flag */
249
+ options?: Array<{ id: string; isDisabled?: boolean; [key: string]: any }>
250
+ }
251
+ requirementsSection?: {
252
+ sectionEnabled?: boolean
253
+ /** Expanded requirement objects with isDisabled flag */
254
+ requirements?: Array<{ id: string; isDisabled?: boolean; [key: string]: any }>
255
+ }
256
+ electiveRequirementsSection?: {
257
+ sectionEnabled?: boolean
258
+ requirements?: Array<{ id: string; isDisabled?: boolean; [key: string]: any }>
259
+ }
260
+ customRequirementsSections?: Array<{
261
+ sectionEnabled?: boolean
262
+ requirements?: Array<{ id: string; isDisabled?: boolean; [key: string]: any }>
263
+ [key: string]: any
264
+ }>
265
+ [key: string]: any
266
+ }
267
+
268
+ /**
269
+ * Certification object as stored inside the cached tenant.
270
+ * Contains expanded requirements (full course objects) and application sections.
271
+ */
272
+ export interface CachedCertification {
273
+ id: string
274
+ customerID: string
275
+ tenantID: string
276
+ /** Expanded course requirements — can be CachedCourse or CachedCourse[] (grouped alternatives) */
277
+ requirements?: (CachedCourse | CachedCourse[])[]
278
+ application?: CachedApplicationSection
279
+ renewal?: {
280
+ application?: CachedApplicationSection
281
+ [key: string]: any
282
+ }
283
+ /** Training providers relevant to this certification */
284
+ trainingProviders?: CachedTrainingProvider[]
285
+ [key: string]: any
286
+ }
287
+
288
+ /**
289
+ * The expanded tenant object stored in the persistent cache.
290
+ * Differs from TenantResponse:
291
+ * - `certifications` array is injected with fully expanded certification objects
292
+ * (including courses, applications with equivalencies/requirements, training providers)
293
+ */
294
+ export interface CachedTenant {
295
+ id: string
296
+ customerID: string
297
+ createdBy?: string
298
+ createdOn?: number
299
+ updatedBy?: string
300
+ updatedOn?: number
301
+ name?: string
302
+ /** Expanded certifications with full course/application/training provider data */
303
+ certifications?: CachedCertification[]
304
+ [key: string]: any
305
+ }