@acorex-platform/extension-project-management-servicem8-client 0.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.
@@ -0,0 +1,50 @@
1
+ import { AXP_INTEGRATION_CONNECTION_ESTABLISHED } from '@acorex-platform/framework-client/common';
2
+ import { AXMIntegrationConnectionManager, toSafeIntegrationConnectionDto } from '@acorex-platform/module-integration-management-client';
3
+ import { A as AXEServiceM8OAuthHttpClient } from './acorex-platform-extension-project-management-servicem8-client-servicem8-oauth-http.client-CsrgXYgR.mjs';
4
+ import * as i0 from '@angular/core';
5
+ import { inject, Injectable } from '@angular/core';
6
+
7
+ class CompleteServiceM8OAuthCommand {
8
+ constructor() {
9
+ this.http = inject(AXEServiceM8OAuthHttpClient);
10
+ this.connectionManager = inject(AXMIntegrationConnectionManager);
11
+ this.establishedHandlers = inject(AXP_INTEGRATION_CONNECTION_ESTABLISHED, { optional: true });
12
+ }
13
+ async execute(input) {
14
+ try {
15
+ const { connectionId } = await this.http.completeOAuth({
16
+ state: input.state,
17
+ code: input.code,
18
+ });
19
+ const connection = await this.connectionManager.getByIdForCurrentUser(connectionId);
20
+ if (!connection) {
21
+ return { success: false, message: { text: 'Connection not found after ServiceM8 OAuth callback.' } };
22
+ }
23
+ const safe = toSafeIntegrationConnectionDto(connection);
24
+ await this.dispatchEstablished(safe);
25
+ return { success: true, kind: 'object', data: safe };
26
+ }
27
+ catch (error) {
28
+ const message = error instanceof Error ? error.message : 'ServiceM8 OAuth callback failed.';
29
+ return { success: false, message: { text: message } };
30
+ }
31
+ }
32
+ async dispatchEstablished(connection) {
33
+ const list = this.establishedHandlers ?? [];
34
+ for (const handler of list) {
35
+ const resolved = handler instanceof Promise ? await handler : handler;
36
+ if (resolved.providerId === connection.providerId) {
37
+ await resolved.onEstablished(connection);
38
+ }
39
+ }
40
+ }
41
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CompleteServiceM8OAuthCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
42
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CompleteServiceM8OAuthCommand, providedIn: 'root' }); }
43
+ }
44
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CompleteServiceM8OAuthCommand, decorators: [{
45
+ type: Injectable,
46
+ args: [{ providedIn: 'root' }]
47
+ }] });
48
+
49
+ export { CompleteServiceM8OAuthCommand };
50
+ //# sourceMappingURL=acorex-platform-extension-project-management-servicem8-client-complete-servicem8-oauth.command-CepIVvQD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acorex-platform-extension-project-management-servicem8-client-complete-servicem8-oauth.command-CepIVvQD.mjs","sources":["../../../../libs/extensions/project-management/servicem8/client/src/lib/features/connect/complete-servicem8-oauth.command.ts"],"sourcesContent":["//#region ---- Complete ServiceM8 OAuth callback ----\n\nimport { AXPCommand, type AXPCommandExecuteResultPromise } from '@acorex-platform/framework-shared/runtime';\nimport type { AXPRuntimeCommandResult, AXPSafeIntegrationConnectionDto } from '@acorex-platform/framework-shared/core';\nimport { AXP_INTEGRATION_CONNECTION_ESTABLISHED } from '@acorex-platform/framework-client/common';\nimport {\n AXMIntegrationConnectionManager,\n toSafeIntegrationConnectionDto,\n} from '@acorex-platform/module-integration-management-client';\nimport { AXEServiceM8OAuthHttpClient } from '../../api/servicem8-oauth-http.client';\nimport { Injectable, inject } from '@angular/core';\n\nexport interface CompleteServiceM8OAuthCommandInput {\n state: string;\n code: string;\n}\n\n@Injectable({ providedIn: 'root' })\nexport class CompleteServiceM8OAuthCommand\n implements AXPCommand<CompleteServiceM8OAuthCommandInput, AXPSafeIntegrationConnectionDto>\n{\n private readonly http = inject(AXEServiceM8OAuthHttpClient);\n private readonly connectionManager = inject(AXMIntegrationConnectionManager);\n private readonly establishedHandlers = inject(AXP_INTEGRATION_CONNECTION_ESTABLISHED, { optional: true });\n\n async execute(\n input: CompleteServiceM8OAuthCommandInput,\n ): AXPCommandExecuteResultPromise<AXPSafeIntegrationConnectionDto> {\n try {\n const { connectionId } = await this.http.completeOAuth({\n state: input.state,\n code: input.code,\n });\n\n const connection = await this.connectionManager.getByIdForCurrentUser(connectionId);\n if (!connection) {\n return { success: false, message: { text: 'Connection not found after ServiceM8 OAuth callback.' } };\n }\n\n const safe = toSafeIntegrationConnectionDto(connection);\n await this.dispatchEstablished(safe);\n return { success: true, kind: 'object', data: safe };\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : 'ServiceM8 OAuth callback failed.';\n return { success: false, message: { text: message } };\n }\n }\n\n private async dispatchEstablished(connection: AXPSafeIntegrationConnectionDto): Promise<void> {\n const list = this.establishedHandlers ?? [];\n for (const handler of list) {\n const resolved = handler instanceof Promise ? await handler : handler;\n if (resolved.providerId === connection.providerId) {\n await resolved.onEstablished(connection);\n }\n }\n }\n}\n\n//#endregion\n"],"names":[],"mappings":";;;;;;MAkBa,6BAA6B,CAAA;AAD1C,IAAA,WAAA,GAAA;AAImB,QAAA,IAAA,CAAA,IAAI,GAAG,MAAM,CAAC,2BAA2B,CAAC;AAC1C,QAAA,IAAA,CAAA,iBAAiB,GAAG,MAAM,CAAC,+BAA+B,CAAC;QAC3D,IAAA,CAAA,mBAAmB,GAAG,MAAM,CAAC,sCAAsC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAkC1G,IAAA;IAhCC,MAAM,OAAO,CACX,KAAyC,EAAA;AAEzC,QAAA,IAAI;YACF,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC;gBACrD,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,IAAI,EAAE,KAAK,CAAC,IAAI;AACjB,aAAA,CAAC;YAEF,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,qBAAqB,CAAC,YAAY,CAAC;YACnF,IAAI,CAAC,UAAU,EAAE;AACf,gBAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,sDAAsD,EAAE,EAAE;YACtG;AAEA,YAAA,MAAM,IAAI,GAAG,8BAA8B,CAAC,UAAU,CAAC;AACvD,YAAA,MAAM,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC;AACpC,YAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE;QACtD;QAAE,OAAO,KAAc,EAAE;AACvB,YAAA,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,kCAAkC;AAC3F,YAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;QACvD;IACF;IAEQ,MAAM,mBAAmB,CAAC,UAA2C,EAAA;AAC3E,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,mBAAmB,IAAI,EAAE;AAC3C,QAAA,KAAK,MAAM,OAAO,IAAI,IAAI,EAAE;AAC1B,YAAA,MAAM,QAAQ,GAAG,OAAO,YAAY,OAAO,GAAG,MAAM,OAAO,GAAG,OAAO;YACrE,IAAI,QAAQ,CAAC,UAAU,KAAK,UAAU,CAAC,UAAU,EAAE;AACjD,gBAAA,MAAM,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC;YAC1C;QACF;IACF;8GAtCW,6BAA6B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA,CAAA;AAA7B,IAAA,SAAA,IAAA,CAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,6BAA6B,cADhB,MAAM,EAAA,CAAA,CAAA;;2FACnB,6BAA6B,EAAA,UAAA,EAAA,CAAA;kBADzC,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;;;"}
@@ -0,0 +1,228 @@
1
+ import { AXP_INTEGRATION_CONNECTION_STATUS, AXP_INTEGRATION_AUTH_METHOD_TYPE } from '@acorex-platform/framework-shared/core';
2
+ import { AXPSessionService } from '@acorex-platform/framework-client/auth';
3
+ import { AXMIntegrationConnectionManager, AXP_INTEGRATION_CREDENTIAL_MASK, toSafeIntegrationConnectionDto, isMaskedIntegrationCredential, hasConfiguredIntegrationCredentials } from '@acorex-platform/module-integration-management-client';
4
+ import { AXPLayoutBuilderService, resolveLayoutBuilderDialogResult } from '@acorex-platform/framework-client/layout/builder';
5
+ import { AXTranslationService } from '@acorex/core/translation';
6
+ import { A as AXEServiceM8OAuthHttpClient } from './acorex-platform-extension-project-management-servicem8-client-servicem8-oauth-http.client-CsrgXYgR.mjs';
7
+ import * as i0 from '@angular/core';
8
+ import { inject, Injectable } from '@angular/core';
9
+ import { trim, defaultTo, isEmpty } from 'lodash-es';
10
+ import { AXE_SERVICEM8_OAUTH_SCOPES, AXE_SERVICEM8_API_BASE_URL } from '@acorex-platform/extension-project-management-servicem8-client/contracts';
11
+
12
+ //#region ---- ServiceM8 OAuth helpers (client) ----
13
+ /** Resolves the demo app path segment used in the OAuth callback URL. */
14
+ function resolveServiceM8OAuthAppName(applicationName) {
15
+ const trimmed = String(applicationName ?? '').trim();
16
+ return trimmed || 'demo';
17
+ }
18
+ //#endregion
19
+
20
+ class ConnectServiceM8Command {
21
+ constructor() {
22
+ this.connectionManager = inject(AXMIntegrationConnectionManager);
23
+ this.layoutBuilder = inject(AXPLayoutBuilderService);
24
+ this.translationService = inject(AXTranslationService);
25
+ this.sessionService = inject(AXPSessionService);
26
+ this.servicem8Http = inject(AXEServiceM8OAuthHttpClient);
27
+ }
28
+ async execute(input = {}) {
29
+ try {
30
+ const providerId = input.providerId ?? 'servicem8';
31
+ const connectionScope = input.connectionScope ?? 'tenant';
32
+ const existing = await this.resolveExistingConnection(providerId, connectionScope, input);
33
+ const isReconfigure = !!existing;
34
+ const existingConfig = (existing?.config ?? {});
35
+ const connectionKey = existing?.connectionKey
36
+ ?? (trim(input.connectionKey) || crypto.randomUUID());
37
+ const title = await this.translationService.translateAsync(isReconfigure
38
+ ? '@extensions-servicem8:integration.connect.dialog.reconfigure-title'
39
+ : '@extensions-servicem8:integration.connect.dialog.title');
40
+ const form = await this.openConnectDialog({
41
+ title: title ?? 'Connect ServiceM8',
42
+ context: {
43
+ displayName: existing?.displayName ?? 'ServiceM8',
44
+ clientId: existingConfig.clientId ?? '',
45
+ clientSecret: isReconfigure ? AXP_INTEGRATION_CREDENTIAL_MASK : '',
46
+ },
47
+ existing,
48
+ existingConfig,
49
+ isReconfigure,
50
+ });
51
+ if (!form) {
52
+ return { success: true, kind: 'object', data: { outcome: 'cancelled' } };
53
+ }
54
+ const credentialsChanged = this.credentialsChanged(existingConfig, form);
55
+ const needsAuthorization = this.needsOAuthAuthorization(existingConfig, credentialsChanged);
56
+ const config = {
57
+ clientId: form.clientId,
58
+ clientSecret: form.clientSecret,
59
+ scope: AXE_SERVICEM8_OAUTH_SCOPES,
60
+ ...(credentialsChanged
61
+ ? {}
62
+ : {
63
+ accessToken: existingConfig.accessToken,
64
+ refreshToken: existingConfig.refreshToken,
65
+ tokenExpiresAt: existingConfig.tokenExpiresAt,
66
+ oauthTokenJson: existingConfig.oauthTokenJson,
67
+ }),
68
+ };
69
+ const connection = await this.connectionManager.upsertConnection({
70
+ providerId,
71
+ authMethodType: AXP_INTEGRATION_AUTH_METHOD_TYPE.OAuth2,
72
+ connectionScope,
73
+ connectionKey,
74
+ displayName: defaultTo(trim(form.displayName), 'ServiceM8'),
75
+ instanceUrl: AXE_SERVICEM8_API_BASE_URL,
76
+ externalAccountName: AXP_INTEGRATION_CREDENTIAL_MASK,
77
+ config: config,
78
+ status: needsAuthorization
79
+ ? AXP_INTEGRATION_CONNECTION_STATUS.PendingAuthorization
80
+ : AXP_INTEGRATION_CONNECTION_STATUS.Connected,
81
+ isRevoked: false,
82
+ isDisabled: false,
83
+ });
84
+ if (needsAuthorization) {
85
+ const appName = resolveServiceM8OAuthAppName(this.sessionService.application?.name);
86
+ const redirectUri = `${window.location.origin}/${appName}/integrations/oauth/callback/${providerId}`;
87
+ const oauth = await this.servicem8Http.startOAuth({
88
+ redirectUri,
89
+ connectionId: connection.id,
90
+ });
91
+ return { success: true, kind: 'object',
92
+ data: { outcome: 'redirect', redirectUrl: oauth.redirectUrl },
93
+ };
94
+ }
95
+ const validation = await this.servicem8Http.validateOAuth({ connectionId: connection.id });
96
+ if (!validation.valid) {
97
+ return {
98
+ success: false,
99
+ message: { text: validation.message ?? 'ServiceM8 connection validation failed. Re-authorize ServiceM8.' },
100
+ };
101
+ }
102
+ const refreshed = await this.connectionManager.getByIdForCurrentUser(connection.id);
103
+ return { success: true, kind: 'object',
104
+ data: {
105
+ outcome: 'connected',
106
+ connection: toSafeIntegrationConnectionDto(refreshed ?? connection),
107
+ },
108
+ };
109
+ }
110
+ catch (error) {
111
+ const message = error instanceof Error ? error.message : 'Failed to connect ServiceM8.';
112
+ return { success: false, message: { text: message } };
113
+ }
114
+ }
115
+ async resolveExistingConnection(providerId, connectionScope, input) {
116
+ if (input.connectionId) {
117
+ const byId = await this.connectionManager.getByIdForCurrentUser(input.connectionId);
118
+ if (!byId) {
119
+ throw new Error('Connection not found or access denied.');
120
+ }
121
+ return byId;
122
+ }
123
+ const connectionKey = trim(input.connectionKey);
124
+ if (!connectionKey) {
125
+ return null;
126
+ }
127
+ return this.connectionManager.findByUniqueKey({
128
+ providerId,
129
+ connectionScope,
130
+ connectionKey,
131
+ });
132
+ }
133
+ credentialsChanged(existingConfig, form) {
134
+ if (trim(form.clientId) !== trim(existingConfig.clientId ?? '')) {
135
+ return true;
136
+ }
137
+ if (!isMaskedIntegrationCredential(form.clientSecret)) {
138
+ return true;
139
+ }
140
+ return false;
141
+ }
142
+ needsOAuthAuthorization(existingConfig, credentialsChanged) {
143
+ if (credentialsChanged) {
144
+ return true;
145
+ }
146
+ return isEmpty(trim(existingConfig.refreshToken ?? ''));
147
+ }
148
+ async openConnectDialog(input) {
149
+ const result = await this.layoutBuilder
150
+ .create()
151
+ .dialog((dialog) => {
152
+ dialog
153
+ .setTitle(input.title)
154
+ .setSize('md')
155
+ .setCloseButton(true)
156
+ .setContext({ ...input.context })
157
+ .content((flex) => {
158
+ flex
159
+ .setDirection('column')
160
+ .setGap('12px')
161
+ .customWidget('text-block-layout', {
162
+ content: '@extensions-servicem8:integration.connect.instructions',
163
+ })
164
+ .formField('@extensions-servicem8:integration.connect.fields.display-name', (field) => {
165
+ field.path('displayName');
166
+ field.textBox({
167
+ placeholder: '@extensions-servicem8:integration.connect.placeholders.display-name',
168
+ });
169
+ })
170
+ .formField('@extensions-servicem8:integration.auth.oauth.client-id.label', (field) => {
171
+ field.path('clientId');
172
+ field.textBox({
173
+ placeholder: '@extensions-servicem8:integration.auth.oauth.client-id.placeholder',
174
+ validations: [{ rule: 'required' }],
175
+ });
176
+ })
177
+ .formField('@extensions-servicem8:integration.auth.oauth.client-secret.label', (field) => {
178
+ field.path('clientSecret');
179
+ field.passwordBox({
180
+ placeholder: '@extensions-servicem8:integration.auth.oauth.client-secret.placeholder',
181
+ revealToggle: true,
182
+ validations: input.isReconfigure ? [] : [{ rule: 'required' }],
183
+ });
184
+ });
185
+ })
186
+ .setActions((actions) => {
187
+ actions
188
+ .cancel('@general:actions.cancel.title')
189
+ .submit(input.isReconfigure && hasConfiguredIntegrationCredentials(input.existing)
190
+ ? '@extensions-servicem8:integration.connect.actions.save'
191
+ : '@extensions-servicem8:integration.connect.actions.save-and-authorize');
192
+ })
193
+ .onAction(async (ref) => {
194
+ if (ref.action() === 'cancel') {
195
+ return null;
196
+ }
197
+ const ctx = ref.context();
198
+ const clientId = trim(ctx.clientId);
199
+ if (!clientId) {
200
+ throw new Error('ServiceM8 App ID is required.');
201
+ }
202
+ let clientSecret = trim(ctx.clientSecret);
203
+ if (isMaskedIntegrationCredential(clientSecret) && input.existing) {
204
+ clientSecret = trim(input.existingConfig.clientSecret ?? '');
205
+ }
206
+ if (!clientSecret) {
207
+ throw new Error('ServiceM8 App Secret is required.');
208
+ }
209
+ return {
210
+ displayName: trim(ctx.displayName) || 'ServiceM8',
211
+ clientId,
212
+ clientSecret,
213
+ };
214
+ });
215
+ })
216
+ .show();
217
+ return resolveLayoutBuilderDialogResult(result);
218
+ }
219
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ConnectServiceM8Command, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
220
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ConnectServiceM8Command, providedIn: 'root' }); }
221
+ }
222
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ConnectServiceM8Command, decorators: [{
223
+ type: Injectable,
224
+ args: [{ providedIn: 'root' }]
225
+ }] });
226
+
227
+ export { ConnectServiceM8Command };
228
+ //# sourceMappingURL=acorex-platform-extension-project-management-servicem8-client-connect-servicem8.command-CkDOwDrg.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acorex-platform-extension-project-management-servicem8-client-connect-servicem8.command-CkDOwDrg.mjs","sources":["../../../../libs/extensions/project-management/servicem8/client/src/lib/features/connect/servicem8-oauth.util.ts","../../../../libs/extensions/project-management/servicem8/client/src/lib/features/connect/connect-servicem8.command.ts"],"sourcesContent":["//#region ---- ServiceM8 OAuth helpers (client) ----\n\n/** Resolves the demo app path segment used in the OAuth callback URL. */\nexport function resolveServiceM8OAuthAppName(applicationName: string | null | undefined): string {\n const trimmed = String(applicationName ?? '').trim();\n return trimmed || 'demo';\n}\n\n//#endregion\n","//#region ---- Connect ServiceM8 (OAuth authorization code) ----\n\nimport { AXPCommand, type AXPCommandExecuteResultPromise } from '@acorex-platform/framework-shared/runtime';\nimport {\n AXP_INTEGRATION_AUTH_METHOD_TYPE,\n AXP_INTEGRATION_CONNECTION_STATUS,\n type AXPRuntimeCommandResult,\n type AXPIntegrationConnectionScope,\n} from '@acorex-platform/framework-shared/core';\nimport { AXPSessionService } from '@acorex-platform/framework-client/auth';\nimport {\n AXMIntegrationConnectionManager,\n AXP_INTEGRATION_CREDENTIAL_MASK,\n hasConfiguredIntegrationCredentials,\n isMaskedIntegrationCredential,\n toSafeIntegrationConnectionDto,\n type ConnectCommandResult,\n} from '@acorex-platform/module-integration-management-client';\nimport {\n AXPLayoutBuilderService,\n resolveLayoutBuilderDialogResult,\n type AXPDialogRef,\n type IActionBuilder,\n type IDialogBuilder,\n type IFlexContainerBuilder,\n type IFormFieldBuilder,\n} from '@acorex-platform/framework-client/layout/builder';\nimport { AXTranslationService } from '@acorex/core/translation';\nimport { AXEServiceM8OAuthHttpClient } from '../../api/servicem8-oauth-http.client';\nimport { Injectable, inject } from '@angular/core';\nimport { defaultTo, isEmpty, trim } from 'lodash-es';\nimport {\n AXE_SERVICEM8_API_BASE_URL,\n AXE_SERVICEM8_OAUTH_SCOPES,\n type AXEServiceM8ConnectionConfig,\n} from '@acorex-platform/extension-project-management-servicem8-client/contracts';\nimport type { AXMIntegrationManagementIntegrationConnectionEntityModel } from '@acorex-platform/module-integration-management-client/contracts';\nimport { resolveServiceM8OAuthAppName } from './servicem8-oauth.util';\n\nexport interface ConnectServiceM8CommandInput {\n providerId?: string;\n connectionScope?: AXPIntegrationConnectionScope;\n /** When set, reconfigure / reconnect this existing connection. */\n connectionId?: string;\n /** Optional explicit key; new connections get a generated key when omitted. */\n connectionKey?: string;\n}\n\ninterface AXEServiceM8ConnectFormContext {\n displayName: string;\n clientId: string;\n clientSecret: string;\n}\n\ninterface AXEServiceM8ConnectFormResult {\n displayName: string;\n clientId: string;\n clientSecret: string;\n}\n\n@Injectable({ providedIn: 'root' })\nexport class ConnectServiceM8Command implements AXPCommand<ConnectServiceM8CommandInput, ConnectCommandResult> {\n private readonly connectionManager = inject(AXMIntegrationConnectionManager);\n private readonly layoutBuilder = inject(AXPLayoutBuilderService);\n private readonly translationService = inject(AXTranslationService);\n private readonly sessionService = inject(AXPSessionService);\n private readonly servicem8Http = inject(AXEServiceM8OAuthHttpClient);\n\n async execute(input: ConnectServiceM8CommandInput = {}): AXPCommandExecuteResultPromise<ConnectCommandResult> {\n try {\n const providerId = input.providerId ?? 'servicem8';\n const connectionScope = input.connectionScope ?? 'tenant';\n const existing = await this.resolveExistingConnection(providerId, connectionScope, input);\n const isReconfigure = !!existing;\n const existingConfig = (existing?.config ?? {}) as AXEServiceM8ConnectionConfig;\n const connectionKey =\n existing?.connectionKey\n ?? (trim(input.connectionKey) || crypto.randomUUID());\n\n const title = await this.translationService.translateAsync(\n isReconfigure\n ? '@extensions-servicem8:integration.connect.dialog.reconfigure-title'\n : '@extensions-servicem8:integration.connect.dialog.title',\n );\n\n const form = await this.openConnectDialog({\n title: title ?? 'Connect ServiceM8',\n context: {\n displayName: existing?.displayName ?? 'ServiceM8',\n clientId: existingConfig.clientId ?? '',\n clientSecret: isReconfigure ? AXP_INTEGRATION_CREDENTIAL_MASK : '',\n },\n existing,\n existingConfig,\n isReconfigure,\n });\n\n if (!form) {\n return { success: true, kind: 'object', data: { outcome: 'cancelled' } };\n }\n\n const credentialsChanged = this.credentialsChanged(existingConfig, form);\n const needsAuthorization = this.needsOAuthAuthorization(existingConfig, credentialsChanged);\n\n const config: AXEServiceM8ConnectionConfig = {\n clientId: form.clientId,\n clientSecret: form.clientSecret,\n scope: AXE_SERVICEM8_OAUTH_SCOPES,\n ...(credentialsChanged\n ? {}\n : {\n accessToken: existingConfig.accessToken,\n refreshToken: existingConfig.refreshToken,\n tokenExpiresAt: existingConfig.tokenExpiresAt,\n oauthTokenJson: existingConfig.oauthTokenJson,\n }),\n };\n\n const connection = await this.connectionManager.upsertConnection({\n providerId,\n authMethodType: AXP_INTEGRATION_AUTH_METHOD_TYPE.OAuth2,\n connectionScope,\n connectionKey,\n displayName: defaultTo(trim(form.displayName), 'ServiceM8'),\n instanceUrl: AXE_SERVICEM8_API_BASE_URL,\n externalAccountName: AXP_INTEGRATION_CREDENTIAL_MASK,\n config: config as Record<string, unknown>,\n status: needsAuthorization\n ? AXP_INTEGRATION_CONNECTION_STATUS.PendingAuthorization\n : AXP_INTEGRATION_CONNECTION_STATUS.Connected,\n isRevoked: false,\n isDisabled: false,\n });\n\n if (needsAuthorization) {\n const appName = resolveServiceM8OAuthAppName(this.sessionService.application?.name);\n const redirectUri = `${window.location.origin}/${appName}/integrations/oauth/callback/${providerId}`;\n const oauth = await this.servicem8Http.startOAuth({\n redirectUri,\n connectionId: connection.id,\n });\n return { success: true, kind: 'object',\n data: { outcome: 'redirect', redirectUrl: oauth.redirectUrl },\n };\n }\n\n const validation = await this.servicem8Http.validateOAuth({ connectionId: connection.id });\n if (!validation.valid) {\n return {\n success: false,\n message: { text: validation.message ?? 'ServiceM8 connection validation failed. Re-authorize ServiceM8.' },\n };\n }\n\n const refreshed = await this.connectionManager.getByIdForCurrentUser(connection.id);\n return { success: true, kind: 'object',\n data: {\n outcome: 'connected',\n connection: toSafeIntegrationConnectionDto(refreshed ?? connection),\n },\n };\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : 'Failed to connect ServiceM8.';\n return { success: false, message: { text: message } };\n }\n }\n\n private async resolveExistingConnection(\n providerId: string,\n connectionScope: AXPIntegrationConnectionScope,\n input: ConnectServiceM8CommandInput,\n ): Promise<AXMIntegrationManagementIntegrationConnectionEntityModel | null> {\n if (input.connectionId) {\n const byId = await this.connectionManager.getByIdForCurrentUser(input.connectionId);\n if (!byId) {\n throw new Error('Connection not found or access denied.');\n }\n return byId;\n }\n\n const connectionKey = trim(input.connectionKey);\n if (!connectionKey) {\n return null;\n }\n\n return this.connectionManager.findByUniqueKey({\n providerId,\n connectionScope,\n connectionKey,\n });\n }\n\n private credentialsChanged(\n existingConfig: AXEServiceM8ConnectionConfig,\n form: AXEServiceM8ConnectFormResult,\n ): boolean {\n if (trim(form.clientId) !== trim(existingConfig.clientId ?? '')) {\n return true;\n }\n if (!isMaskedIntegrationCredential(form.clientSecret)) {\n return true;\n }\n return false;\n }\n\n private needsOAuthAuthorization(\n existingConfig: AXEServiceM8ConnectionConfig,\n credentialsChanged: boolean,\n ): boolean {\n if (credentialsChanged) {\n return true;\n }\n return isEmpty(trim(existingConfig.refreshToken ?? ''));\n }\n\n private async openConnectDialog(input: {\n title: string;\n context: AXEServiceM8ConnectFormContext;\n existing: AXMIntegrationManagementIntegrationConnectionEntityModel | null;\n existingConfig: AXEServiceM8ConnectionConfig;\n isReconfigure: boolean;\n }): Promise<AXEServiceM8ConnectFormResult | null> {\n const result: unknown = await this.layoutBuilder\n .create()\n .dialog<AXEServiceM8ConnectFormResult | null>((dialog: IDialogBuilder<AXEServiceM8ConnectFormResult | null>) => {\n dialog\n .setTitle(input.title)\n .setSize('md')\n .setCloseButton(true)\n .setContext({ ...input.context })\n .content((flex: IFlexContainerBuilder) => {\n flex\n .setDirection('column')\n .setGap('12px')\n .customWidget('text-block-layout', {\n content: '@extensions-servicem8:integration.connect.instructions',\n })\n .formField('@extensions-servicem8:integration.connect.fields.display-name', (field: IFormFieldBuilder) => {\n field.path('displayName');\n field.textBox({\n placeholder: '@extensions-servicem8:integration.connect.placeholders.display-name',\n });\n })\n .formField('@extensions-servicem8:integration.auth.oauth.client-id.label', (field) => {\n field.path('clientId');\n field.textBox({\n placeholder: '@extensions-servicem8:integration.auth.oauth.client-id.placeholder',\n validations: [{ rule: 'required' }],\n });\n })\n .formField('@extensions-servicem8:integration.auth.oauth.client-secret.label', (field) => {\n field.path('clientSecret');\n field.passwordBox({\n placeholder: '@extensions-servicem8:integration.auth.oauth.client-secret.placeholder',\n revealToggle: true,\n validations: input.isReconfigure ? [] : [{ rule: 'required' }],\n });\n });\n })\n .setActions((actions: IActionBuilder) => {\n actions\n .cancel('@general:actions.cancel.title')\n .submit(\n input.isReconfigure && hasConfiguredIntegrationCredentials(input.existing)\n ? '@extensions-servicem8:integration.connect.actions.save'\n : '@extensions-servicem8:integration.connect.actions.save-and-authorize',\n );\n })\n .onAction(async (ref: AXPDialogRef) => {\n if (ref.action() === 'cancel') {\n return null;\n }\n\n const ctx = ref.context() as AXEServiceM8ConnectFormContext;\n const clientId = trim(ctx.clientId);\n if (!clientId) {\n throw new Error('ServiceM8 App ID is required.');\n }\n\n let clientSecret = trim(ctx.clientSecret);\n if (isMaskedIntegrationCredential(clientSecret) && input.existing) {\n clientSecret = trim(input.existingConfig.clientSecret ?? '');\n }\n if (!clientSecret) {\n throw new Error('ServiceM8 App Secret is required.');\n }\n\n return {\n displayName: trim(ctx.displayName) || 'ServiceM8',\n clientId,\n clientSecret,\n };\n });\n })\n .show();\n\n return resolveLayoutBuilderDialogResult<AXEServiceM8ConnectFormResult>(result);\n }\n}\n\n//#endregion\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,112 @@
1
+ export { AXE_PM_PROJECT_SYNC_STRATEGIES as AXE_SERVICEM8_PROJECT_SYNC_STRATEGIES } from '@acorex-platform/extension-project-management-core-contracts';
2
+
3
+ //#region ---- Module Config ----
4
+ const config = {
5
+ i18n: 'extensions-servicem8',
6
+ };
7
+ const MODULE_NAME = 'ServiceM8Extension';
8
+ const RootConfig = {
9
+ config,
10
+ module: {
11
+ name: MODULE_NAME,
12
+ title: `@${config.i18n}:integration.display-name`,
13
+ icon: 'fa-light fa-briefcase',
14
+ },
15
+ };
16
+ //#endregion
17
+
18
+ //#region ---- Command Keys ----
19
+ const AXEServiceM8CommandKeys = {
20
+ ConnectionConnect: 'servicem8-extension.Connection:Connect',
21
+ ConnectionCompleteOAuth: 'servicem8-extension.Connection:CompleteOAuth',
22
+ OAuthStart: 'servicem8-extension.OAuth:Start',
23
+ OAuthComplete: 'servicem8-extension.OAuth:Complete',
24
+ OAuthValidate: 'servicem8-extension.OAuth:Validate',
25
+ ProjectImportFromServiceM8: 'servicem8-extension.Project:ImportFromServiceM8',
26
+ ProjectSync: 'servicem8-extension.Project:Sync',
27
+ JobsPull: 'servicem8-extension.Jobs:Pull',
28
+ JobsRefresh: 'servicem8-extension.Jobs:Refresh',
29
+ };
30
+ //#endregion
31
+
32
+ //#region ---- Menu Keys ----
33
+ const AXEServiceM8MenuKeys = {
34
+ ServiceM8ExtensionConfig: 'servicem8-extension:Menu:Configuration',
35
+ };
36
+ //#endregion
37
+
38
+ //#region ---- Permission Keys ----
39
+ /**
40
+ * Administration capabilities used by this extension.
41
+ * Domain capability keys for this module. Root groups live in `AXPCommonPermissionGroupKeys` (common contracts).
42
+ */
43
+ const AXPAdministrationPermissionKeys = {
44
+ Access: 'Administration:Permission:Access',
45
+ MaintainUsersAndRoles: 'Administration:Permission:MaintainUsersAndRoles',
46
+ ManageSessions: 'Administration:Permission:ManageSessions',
47
+ MaintainPolicies: 'Administration:Permission:MaintainPolicies',
48
+ ViewIntegrations: 'Administration:Permission:ViewIntegrations',
49
+ MaintainConnections: 'Administration:Permission:MaintainConnections',
50
+ ManageIntegrationActions: 'Administration:Permission:ManageIntegrationActions',
51
+ MaintainWorkflows: 'Administration:Permission:MaintainWorkflows',
52
+ ManageWorkflowActions: 'Administration:Permission:ManageWorkflowActions',
53
+ ViewRules: 'Administration:Permission:ViewRules',
54
+ MaintainRules: 'Administration:Permission:MaintainRules',
55
+ ManageRuleActions: 'Administration:Permission:ManageRuleActions',
56
+ MaintainAi: 'Administration:Permission:MaintainAi',
57
+ UseAiAssist: 'Administration:Permission:UseAiAssist',
58
+ MaintainDashboards: 'Administration:Permission:MaintainDashboards',
59
+ ViewJobs: 'Administration:Permission:ViewJobs',
60
+ ManageJobActions: 'Administration:Permission:ManageJobActions',
61
+ ViewObservability: 'Administration:Permission:ViewObservability',
62
+ MaintainObservability: 'Administration:Permission:MaintainObservability',
63
+ };
64
+ /**
65
+ * @deprecated Prefer {@link AXPAdministrationPermissionKeys}.
66
+ * Legacy nested shape kept so existing imports compile during migration.
67
+ */
68
+ const AXEServiceM8Permissions = {
69
+ ServiceM8Extension: {
70
+ Management: AXPAdministrationPermissionKeys.MaintainConnections,
71
+ Catalog: {
72
+ View: AXPAdministrationPermissionKeys.ViewIntegrations,
73
+ },
74
+ ProjectImport: {
75
+ Execute: AXPAdministrationPermissionKeys.ManageIntegrationActions,
76
+ },
77
+ ProjectSync: {
78
+ Execute: AXPAdministrationPermissionKeys.ManageIntegrationActions,
79
+ },
80
+ },
81
+ };
82
+ //#endregion
83
+
84
+ //#region ---- ServiceM8 tenant connection config ----
85
+ //#endregion
86
+
87
+ //#region ---- ServiceM8 OAuth API (client ↔ server) ----
88
+ //#endregion
89
+
90
+ //#region ---- Remote ServiceM8 job DTO ----
91
+ //#endregion
92
+
93
+ //#region ---- ServiceM8 API / OAuth constants ----
94
+ /** Public OAuth authorize endpoint (fixed host). */
95
+ const AXE_SERVICEM8_OAUTH_AUTHORIZE_URL = 'https://go.servicem8.com/oauth/authorize';
96
+ /** Public OAuth token endpoint (authorization_code + refresh_token). */
97
+ const AXE_SERVICEM8_OAUTH_TOKEN_URL = 'https://go.servicem8.com/oauth/access_token';
98
+ /** REST API base URL stored on connection `instanceUrl`. */
99
+ const AXE_SERVICEM8_API_BASE_URL = 'https://api.servicem8.com';
100
+ /**
101
+ * OAuth scopes for connect.
102
+ * `manage_jobs` is included so push sync can be added later without re-consent.
103
+ */
104
+ const AXE_SERVICEM8_OAUTH_SCOPES = 'read_jobs manage_jobs vendor';
105
+ //#endregion
106
+
107
+ /**
108
+ * Generated bundle index. Do not edit.
109
+ */
110
+
111
+ export { AXEServiceM8CommandKeys, AXEServiceM8MenuKeys, AXEServiceM8Permissions, AXE_SERVICEM8_API_BASE_URL, AXE_SERVICEM8_OAUTH_AUTHORIZE_URL, AXE_SERVICEM8_OAUTH_SCOPES, AXE_SERVICEM8_OAUTH_TOKEN_URL, AXPAdministrationPermissionKeys, RootConfig };
112
+ //# sourceMappingURL=acorex-platform-extension-project-management-servicem8-client-contracts.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acorex-platform-extension-project-management-servicem8-client-contracts.mjs","sources":["../../../../libs/extensions/project-management/servicem8/contracts/src/root-config.ts","../../../../libs/extensions/project-management/servicem8/contracts/src/commands.keys.ts","../../../../libs/extensions/project-management/servicem8/contracts/src/menu.keys.ts","../../../../libs/extensions/project-management/servicem8/contracts/src/permission.keys.ts","../../../../libs/extensions/project-management/servicem8/contracts/src/connection.types.ts","../../../../libs/extensions/project-management/servicem8/contracts/src/oauth.api.types.ts","../../../../libs/extensions/project-management/servicem8/contracts/src/project-sync.api.types.ts","../../../../libs/extensions/project-management/servicem8/contracts/src/servicem8.constants.ts","../../../../libs/extensions/project-management/servicem8/contracts/src/acorex-platform-extension-project-management-servicem8-client-contracts.ts"],"sourcesContent":["//#region ---- Module Config ----\n\nconst config = {\n i18n: 'extensions-servicem8',\n};\n\nconst MODULE_NAME = 'ServiceM8Extension';\n\nexport const RootConfig = {\n config,\n module: {\n name: MODULE_NAME,\n title: `@${config.i18n}:integration.display-name`,\n icon: 'fa-light fa-briefcase',\n },\n};\n\n//#endregion\n","//#region ---- Command Keys ----\n\nexport const AXEServiceM8CommandKeys = {\n ConnectionConnect: 'servicem8-extension.Connection:Connect',\n ConnectionCompleteOAuth: 'servicem8-extension.Connection:CompleteOAuth',\n OAuthStart: 'servicem8-extension.OAuth:Start',\n OAuthComplete: 'servicem8-extension.OAuth:Complete',\n OAuthValidate: 'servicem8-extension.OAuth:Validate',\n ProjectImportFromServiceM8: 'servicem8-extension.Project:ImportFromServiceM8',\n ProjectSync: 'servicem8-extension.Project:Sync',\n JobsPull: 'servicem8-extension.Jobs:Pull',\n JobsRefresh: 'servicem8-extension.Jobs:Refresh',\n} as const;\n\n//#endregion\n","//#region ---- Menu Keys ----\n\nexport const AXEServiceM8MenuKeys = {\n ServiceM8ExtensionConfig: 'servicem8-extension:Menu:Configuration',\n} as const;\n\n//#endregion\n","//#region ---- Permission Keys ----\n\n/**\n * Administration capabilities used by this extension.\n * Domain capability keys for this module. Root groups live in `AXPCommonPermissionGroupKeys` (common contracts).\n */\nexport const AXPAdministrationPermissionKeys = {\n Access: 'Administration:Permission:Access',\n MaintainUsersAndRoles: 'Administration:Permission:MaintainUsersAndRoles',\n ManageSessions: 'Administration:Permission:ManageSessions',\n MaintainPolicies: 'Administration:Permission:MaintainPolicies',\n ViewIntegrations: 'Administration:Permission:ViewIntegrations',\n MaintainConnections: 'Administration:Permission:MaintainConnections',\n ManageIntegrationActions: 'Administration:Permission:ManageIntegrationActions',\n MaintainWorkflows: 'Administration:Permission:MaintainWorkflows',\n ManageWorkflowActions: 'Administration:Permission:ManageWorkflowActions',\n ViewRules: 'Administration:Permission:ViewRules',\n MaintainRules: 'Administration:Permission:MaintainRules',\n ManageRuleActions: 'Administration:Permission:ManageRuleActions',\n MaintainAi: 'Administration:Permission:MaintainAi',\n UseAiAssist: 'Administration:Permission:UseAiAssist',\n MaintainDashboards: 'Administration:Permission:MaintainDashboards',\n ViewJobs: 'Administration:Permission:ViewJobs',\n ManageJobActions: 'Administration:Permission:ManageJobActions',\n ViewObservability: 'Administration:Permission:ViewObservability',\n MaintainObservability: 'Administration:Permission:MaintainObservability',\n} as const;\n\n/**\n * @deprecated Prefer {@link AXPAdministrationPermissionKeys}.\n * Legacy nested shape kept so existing imports compile during migration.\n */\nexport const AXEServiceM8Permissions = {\n ServiceM8Extension: {\n Management: AXPAdministrationPermissionKeys.MaintainConnections,\n Catalog: {\n View: AXPAdministrationPermissionKeys.ViewIntegrations,\n },\n ProjectImport: {\n Execute: AXPAdministrationPermissionKeys.ManageIntegrationActions,\n },\n ProjectSync: {\n Execute: AXPAdministrationPermissionKeys.ManageIntegrationActions,\n },\n },\n} as const;\n\n//#endregion\n","//#region ---- ServiceM8 tenant connection config ----\n\n/** Stored on IntegrationManagement connection `config` for tenant-scoped ServiceM8. */\nexport interface AXEServiceM8ConnectionConfig {\n /** OAuth2 App ID from the ServiceM8 developer add-on. */\n clientId?: string;\n /** OAuth2 App Secret from the ServiceM8 developer add-on. */\n clientSecret?: string;\n /** Space-separated scopes granted at authorize time. */\n scope?: string;\n /** Current bearer token (authorization-code flow). */\n accessToken?: string;\n /** Refresh token from ServiceM8 token response. */\n refreshToken?: string;\n /** ISO timestamp when `accessToken` expires. */\n tokenExpiresAt?: string;\n /** Full ServiceM8 token JSON response. */\n oauthTokenJson?: string;\n /** Background job pull sync state (server-managed). */\n jobsSync?: AXEServiceM8JobsSyncState;\n}\n\n/** ServiceM8 job pull sync state stored on the tenant connection `config.jobsSync`. */\nexport interface AXEServiceM8JobsSyncState {\n /** ISO timestamp — last successful job pull (incremental sync watermark). */\n lastSyncAt?: string;\n /** ISO timestamp — last full snapshot reconcile (deactivation pass). */\n lastFullReconcileAt?: string;\n}\n\n//#endregion\n","//#region ---- ServiceM8 OAuth API (client ↔ server) ----\n\nexport interface AXEServiceM8OAuthStartRequest {\n redirectUri: string;\n connectionId?: string;\n}\n\nexport interface AXEServiceM8OAuthStartResponse {\n redirectUrl: string;\n state: string;\n}\n\nexport interface AXEServiceM8OAuthCompleteRequest {\n state: string;\n code: string;\n}\n\nexport interface AXEServiceM8OAuthCompleteResponse {\n connectionId: string;\n}\n\nexport interface AXEServiceM8OAuthValidateRequest {\n connectionId?: string;\n}\n\nexport interface AXEServiceM8OAuthValidateResponse {\n valid: boolean;\n message?: string;\n}\n\n//#endregion\n","//#region ---- Remote ServiceM8 job DTO ----\n\nimport type { AXEServiceM8ProjectSyncStrategy } from './sync.types';\n\n/** Subset of ServiceM8 `job.json` fields used for cache / import / sync. */\nexport interface AXEServiceM8RemoteJob {\n uuid: string;\n generated_job_id?: string | null;\n job_description?: string | null;\n job_address?: string | null;\n status?: string | null;\n date?: string | null;\n edit_date?: string | null;\n company_uuid?: string | null;\n company_name?: string | null;\n purchase_order_number?: string | null;\n active?: number | boolean | null;\n billing_address?: string | null;\n work_done_description?: string | null;\n}\n\n//#endregion\n\n//#region ---- API request / response ----\n\nexport interface AXEServiceM8PullJobsRequest {\n /** Maximum ServiceM8 jobs to pull into the cache (default 250, max 1000). */\n maxJobs?: number;\n /**\n * When true, runs a full snapshot (no incremental filter) and reconciles deactivations.\n * When false (default for schedules), pulls jobs modified since the last successful sync.\n */\n force?: boolean;\n}\n\nexport interface AXEServiceM8PullJobsResponse {\n upserted: number;\n deactivated: number;\n /** True when incremental pull found no modified jobs in ServiceM8. */\n skipped?: boolean;\n /** Whether this run used incremental or full snapshot mode. */\n syncMode?: 'incremental' | 'full';\n}\n\nexport interface AXEServiceM8ImportJobRequest {\n remoteRecordId: string;\n projectTypeId: string;\n syncStrategy: AXEServiceM8ProjectSyncStrategy;\n}\n\nexport interface AXEServiceM8ImportJobResponse {\n platformProjectId: string;\n created: boolean;\n}\n\nexport interface AXEServiceM8SyncPlatformProjectRequest {\n platformProjectId: string;\n}\n\n//#endregion\n","//#region ---- ServiceM8 API / OAuth constants ----\n\n/** Public OAuth authorize endpoint (fixed host). */\nexport const AXE_SERVICEM8_OAUTH_AUTHORIZE_URL = 'https://go.servicem8.com/oauth/authorize';\n\n/** Public OAuth token endpoint (authorization_code + refresh_token). */\nexport const AXE_SERVICEM8_OAUTH_TOKEN_URL = 'https://go.servicem8.com/oauth/access_token';\n\n/** REST API base URL stored on connection `instanceUrl`. */\nexport const AXE_SERVICEM8_API_BASE_URL = 'https://api.servicem8.com';\n\n/**\n * OAuth scopes for connect.\n * `manage_jobs` is included so push sync can be added later without re-consent.\n */\nexport const AXE_SERVICEM8_OAUTH_SCOPES = 'read_jobs manage_jobs vendor';\n\n//#endregion\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;AAAA;AAEA,MAAM,MAAM,GAAG;AACb,IAAA,IAAI,EAAE,sBAAsB;CAC7B;AAED,MAAM,WAAW,GAAG,oBAAoB;AAEjC,MAAM,UAAU,GAAG;IACxB,MAAM;AACN,IAAA,MAAM,EAAE;AACN,QAAA,IAAI,EAAE,WAAW;AACjB,QAAA,KAAK,EAAE,CAAA,CAAA,EAAI,MAAM,CAAC,IAAI,CAAA,yBAAA,CAA2B;AACjD,QAAA,IAAI,EAAE,uBAAuB;AAC9B,KAAA;;AAGH;;ACjBA;AAEO,MAAM,uBAAuB,GAAG;AACrC,IAAA,iBAAiB,EAAE,wCAAwC;AAC3D,IAAA,uBAAuB,EAAE,8CAA8C;AACvE,IAAA,UAAU,EAAE,iCAAiC;AAC7C,IAAA,aAAa,EAAE,oCAAoC;AACnD,IAAA,aAAa,EAAE,oCAAoC;AACnD,IAAA,0BAA0B,EAAE,iDAAiD;AAC7E,IAAA,WAAW,EAAE,kCAAkC;AAC/C,IAAA,QAAQ,EAAE,+BAA+B;AACzC,IAAA,WAAW,EAAE,kCAAkC;;AAGjD;;ACdA;AAEO,MAAM,oBAAoB,GAAG;AAClC,IAAA,wBAAwB,EAAE,wCAAwC;;AAGpE;;ACNA;AAEA;;;AAGG;AACI,MAAM,+BAA+B,GAAG;AAC7C,IAAA,MAAM,EAAE,kCAAkC;AAC1C,IAAA,qBAAqB,EAAE,iDAAiD;AACxE,IAAA,cAAc,EAAE,0CAA0C;AAC1D,IAAA,gBAAgB,EAAE,4CAA4C;AAC9D,IAAA,gBAAgB,EAAE,4CAA4C;AAC9D,IAAA,mBAAmB,EAAE,+CAA+C;AACpE,IAAA,wBAAwB,EAAE,oDAAoD;AAC9E,IAAA,iBAAiB,EAAE,6CAA6C;AAChE,IAAA,qBAAqB,EAAE,iDAAiD;AACxE,IAAA,SAAS,EAAE,qCAAqC;AAChD,IAAA,aAAa,EAAE,yCAAyC;AACxD,IAAA,iBAAiB,EAAE,6CAA6C;AAChE,IAAA,UAAU,EAAE,sCAAsC;AAClD,IAAA,WAAW,EAAE,uCAAuC;AACpD,IAAA,kBAAkB,EAAE,8CAA8C;AAClE,IAAA,QAAQ,EAAE,oCAAoC;AAC9C,IAAA,gBAAgB,EAAE,4CAA4C;AAC9D,IAAA,iBAAiB,EAAE,6CAA6C;AAChE,IAAA,qBAAqB,EAAE,iDAAiD;;AAG1E;;;AAGG;AACI,MAAM,uBAAuB,GAAG;AACrC,IAAA,kBAAkB,EAAE;QAClB,UAAU,EAAE,+BAA+B,CAAC,mBAAmB;AAC/D,QAAA,OAAO,EAAE;YACP,IAAI,EAAE,+BAA+B,CAAC,gBAAgB;AACvD,SAAA;AACD,QAAA,aAAa,EAAE;YACb,OAAO,EAAE,+BAA+B,CAAC,wBAAwB;AAClE,SAAA;AACD,QAAA,WAAW,EAAE;YACX,OAAO,EAAE,+BAA+B,CAAC,wBAAwB;AAClE,SAAA;AACF,KAAA;;AAGH;;AC/CA;AA8BA;;AC9BA;AA8BA;;AC9BA;AA2DA;;AC3DA;AAEA;AACO,MAAM,iCAAiC,GAAG;AAEjD;AACO,MAAM,6BAA6B,GAAG;AAE7C;AACO,MAAM,0BAA0B,GAAG;AAE1C;;;AAGG;AACI,MAAM,0BAA0B,GAAG;AAE1C;;ACjBA;;AAEG;;;;"}
@@ -0,0 +1,36 @@
1
+ import { commandMessageTextForError } from '@acorex-platform/framework-client/common';
2
+ import { AXPCommandService } from '@acorex-platform/framework-client/runtime';
3
+ import { AXEServiceM8CommandKeys } from '@acorex-platform/extension-project-management-servicem8-client/contracts';
4
+ import * as i0 from '@angular/core';
5
+ import { inject, Injectable } from '@angular/core';
6
+
7
+ class AXEServiceM8OAuthHttpClient {
8
+ constructor() {
9
+ this.commandService = inject(AXPCommandService);
10
+ }
11
+ startOAuth(body) {
12
+ return this.runCommand(AXEServiceM8CommandKeys.OAuthStart, body);
13
+ }
14
+ completeOAuth(body) {
15
+ return this.runCommand(AXEServiceM8CommandKeys.OAuthComplete, body);
16
+ }
17
+ validateOAuth(body = {}) {
18
+ return this.runCommand(AXEServiceM8CommandKeys.OAuthValidate, body);
19
+ }
20
+ async runCommand(key, input) {
21
+ const result = await this.commandService.execute(key, input);
22
+ if (!result?.success) {
23
+ throw new Error(commandMessageTextForError(result?.message?.text) || `ServiceM8 OAuth command failed: ${key}`);
24
+ }
25
+ return result.data;
26
+ }
27
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEServiceM8OAuthHttpClient, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
28
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEServiceM8OAuthHttpClient, providedIn: 'root' }); }
29
+ }
30
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEServiceM8OAuthHttpClient, decorators: [{
31
+ type: Injectable,
32
+ args: [{ providedIn: 'root' }]
33
+ }] });
34
+
35
+ export { AXEServiceM8OAuthHttpClient as A };
36
+ //# sourceMappingURL=acorex-platform-extension-project-management-servicem8-client-servicem8-oauth-http.client-CsrgXYgR.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acorex-platform-extension-project-management-servicem8-client-servicem8-oauth-http.client-CsrgXYgR.mjs","sources":["../../../../libs/extensions/project-management/servicem8/client/src/lib/api/servicem8-oauth-http.client.ts"],"sourcesContent":["import { commandMessageTextForError } from '@acorex-platform/framework-client/common';\nimport { AXPCommandService } from '@acorex-platform/framework-client/runtime';\nimport {\n AXEServiceM8CommandKeys,\n type AXEServiceM8OAuthCompleteRequest,\n type AXEServiceM8OAuthCompleteResponse,\n type AXEServiceM8OAuthStartRequest,\n type AXEServiceM8OAuthStartResponse,\n type AXEServiceM8OAuthValidateRequest,\n type AXEServiceM8OAuthValidateResponse,\n} from '@acorex-platform/extension-project-management-servicem8-client/contracts';\nimport { inject, Injectable } from '@angular/core';\n\n@Injectable({ providedIn: 'root' })\nexport class AXEServiceM8OAuthHttpClient {\n private readonly commandService = inject(AXPCommandService);\n\n startOAuth(body: AXEServiceM8OAuthStartRequest): Promise<AXEServiceM8OAuthStartResponse> {\n return this.runCommand(AXEServiceM8CommandKeys.OAuthStart, body);\n }\n\n completeOAuth(body: AXEServiceM8OAuthCompleteRequest): Promise<AXEServiceM8OAuthCompleteResponse> {\n return this.runCommand(AXEServiceM8CommandKeys.OAuthComplete, body);\n }\n\n validateOAuth(body: AXEServiceM8OAuthValidateRequest = {}): Promise<AXEServiceM8OAuthValidateResponse> {\n return this.runCommand(AXEServiceM8CommandKeys.OAuthValidate, body);\n }\n\n private async runCommand<TInput, TResult>(key: string, input: TInput): Promise<TResult> {\n const result = await this.commandService.execute<TInput, TResult>(key, input);\n if (!result?.success) {\n throw new Error(commandMessageTextForError(result?.message?.text) || `ServiceM8 OAuth command failed: ${key}`);\n }\n return result.data as TResult;\n }\n}\n"],"names":[],"mappings":";;;;;;MAca,2BAA2B,CAAA;AADxC,IAAA,WAAA,GAAA;AAEmB,QAAA,IAAA,CAAA,cAAc,GAAG,MAAM,CAAC,iBAAiB,CAAC;AAqB5D,IAAA;AAnBC,IAAA,UAAU,CAAC,IAAmC,EAAA;QAC5C,OAAO,IAAI,CAAC,UAAU,CAAC,uBAAuB,CAAC,UAAU,EAAE,IAAI,CAAC;IAClE;AAEA,IAAA,aAAa,CAAC,IAAsC,EAAA;QAClD,OAAO,IAAI,CAAC,UAAU,CAAC,uBAAuB,CAAC,aAAa,EAAE,IAAI,CAAC;IACrE;IAEA,aAAa,CAAC,OAAyC,EAAE,EAAA;QACvD,OAAO,IAAI,CAAC,UAAU,CAAC,uBAAuB,CAAC,aAAa,EAAE,IAAI,CAAC;IACrE;AAEQ,IAAA,MAAM,UAAU,CAAkB,GAAW,EAAE,KAAa,EAAA;AAClE,QAAA,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAkB,GAAG,EAAE,KAAK,CAAC;AAC7E,QAAA,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE;AACpB,YAAA,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,CAAA,gCAAA,EAAmC,GAAG,CAAA,CAAE,CAAC;QAChH;QACA,OAAO,MAAM,CAAC,IAAe;IAC/B;8GArBW,2BAA2B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA,CAAA;AAA3B,IAAA,SAAA,IAAA,CAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,2BAA2B,cADd,MAAM,EAAA,CAAA,CAAA;;2FACnB,2BAA2B,EAAA,UAAA,EAAA,CAAA;kBADvC,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;;;"}
@@ -0,0 +1,411 @@
1
+ import { AXMIntegrationManagementModule } from '@acorex-platform/module-integration-management-client';
2
+ import { AXPIntegrationRuntimeService, AXP_INTEGRATION_SECRET_STORE, AXPSettingsService, AXP_INTEGRATION_RUNTIME_PROVIDER, AXP_MENU_PROVIDER, AXP_INTEGRATION_PROVIDER } from '@acorex-platform/framework-client/common';
3
+ import { AXP_LOGO_PROVIDER, AXP_MODULE_MANIFEST_PROVIDER, AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER } from '@acorex-platform/framework-client/core';
4
+ import { AXPEntityService, ensureListActions, actionExists, AXP_ENTITY_MODIFIER } from '@acorex-platform/framework-client/layout/entity';
5
+ import { provideCommandSetups } from '@acorex-platform/framework-client/runtime';
6
+ import * as i0 from '@angular/core';
7
+ import { inject, Injectable, NgModule } from '@angular/core';
8
+ import { AXEServiceM8CommandKeys, RootConfig as RootConfig$1, AXPAdministrationPermissionKeys, AXEServiceM8MenuKeys } from '@acorex-platform/extension-project-management-servicem8-client/contracts';
9
+ import { AXE_PM_INTEGRATION_PROVIDER_SERVICEM8 } from '@acorex-platform/extension-project-management-core-client/contracts';
10
+ import { RootConfig } from '@acorex-platform/module-project-management-client/contracts';
11
+ import { AXPSystemActionType, AXPWidgetsList, AXP_INTEGRATION_AUTH_METHOD_TYPE, AXP_INTEGRATION_CONNECTION_SCOPE, AXP_INTEGRATION_CAPABILITY, AXPRegionalSetting, AXP_INTEGRATION_CONNECTION_STATUS } from '@acorex-platform/framework-shared/core';
12
+ import { AXPEntityCommandScope } from '@acorex-platform/framework-shared/entity';
13
+ import { AXFormatService } from '@acorex/core/format';
14
+ import { trim, defaultTo } from 'lodash-es';
15
+ import { AXPCommonMenuKeys } from '@acorex-platform/module-common-client/contracts';
16
+
17
+ const AXE_SERVICEM8_EXPRESSION_SCOPE = 'ServiceM8Extension';
18
+
19
+ //#region ---- Imports ----
20
+ //#endregion
21
+ class AXEServiceM8ProjectLinkExpressionEvaluatorScopeProvider {
22
+ constructor() {
23
+ this.entityService = inject(AXPEntityService);
24
+ this.integrationRuntime = inject(AXPIntegrationRuntimeService);
25
+ }
26
+ async provide(context) {
27
+ context.addScope(AXE_SERVICEM8_EXPRESSION_SCOPE, {
28
+ hasProjectLink: async (platformProjectIdRaw) => {
29
+ const platformProjectId = typeof platformProjectIdRaw === 'string' ? platformProjectIdRaw.trim() : '';
30
+ if (!platformProjectId) {
31
+ return false;
32
+ }
33
+ const project = (await this.entityService
34
+ .withEntity(RootConfig.module.name, RootConfig.entities.project.name)
35
+ .data()
36
+ .byKey(platformProjectId));
37
+ return project?.integrationRef?.source === AXE_PM_INTEGRATION_PROVIDER_SERVICEM8;
38
+ },
39
+ hasServiceM8Connection: async () => {
40
+ try {
41
+ await this.integrationRuntime.getConnection({ providerId: 'servicem8', connectionScope: 'tenant' });
42
+ return true;
43
+ }
44
+ catch {
45
+ return false;
46
+ }
47
+ },
48
+ });
49
+ }
50
+ }
51
+ //#endregion
52
+
53
+ //#region ---- Imports ----
54
+ //#endregion
55
+ const IMPORT_ORDER = 36;
56
+ const SYNC_ORDER = 37;
57
+ function buildImportAction() {
58
+ return {
59
+ name: AXEServiceM8CommandKeys.ProjectImportFromServiceM8,
60
+ title: '@extensions-servicem8:actions.import-from-servicem8.title',
61
+ command: { name: AXEServiceM8CommandKeys.ProjectImportFromServiceM8 },
62
+ priority: 'secondary',
63
+ type: AXPSystemActionType.Import,
64
+ icon: 'fa-light fa-briefcase',
65
+ scope: AXPEntityCommandScope.TypeLevel,
66
+ order: IMPORT_ORDER,
67
+ };
68
+ }
69
+ function buildSyncAction() {
70
+ return {
71
+ name: AXEServiceM8CommandKeys.ProjectSync,
72
+ title: '@extensions-servicem8:actions.sync.title',
73
+ command: {
74
+ name: AXEServiceM8CommandKeys.ProjectSync,
75
+ options: { platformProjectId: '{{ context.eval("id") }}' },
76
+ },
77
+ priority: 'secondary',
78
+ type: AXPSystemActionType.Sync,
79
+ icon: 'fa-light fa-arrows-rotate',
80
+ scope: AXPEntityCommandScope.Individual,
81
+ order: SYNC_ORDER,
82
+ hidden: `{{ !(await ${AXE_SERVICEM8_EXPRESSION_SCOPE}.hasProjectLink(context.eval("id"))) }}`,
83
+ };
84
+ }
85
+ /** Adds ServiceM8 import/sync actions to ProjectManagement.Project views. */
86
+ const AXEServiceM8ProjectEntityModifier = {
87
+ entityName: RootConfig.entities.project.name,
88
+ order: 50,
89
+ modifier: (ctx) => {
90
+ if (ctx.module.get() !== RootConfig.module.name) {
91
+ return;
92
+ }
93
+ ensureListActions(ctx);
94
+ ctx.interfaces.update((interfaces) => {
95
+ const next = (interfaces ?? {});
96
+ const master = (next['master'] ?? {});
97
+ const list = (master['list'] ?? { actions: [], views: [] });
98
+ const listActions = (list['actions'] ?? []);
99
+ if (!actionExists(listActions, AXEServiceM8CommandKeys.ProjectImportFromServiceM8)) {
100
+ listActions.push(buildImportAction());
101
+ }
102
+ list['actions'] = listActions;
103
+ master['list'] = list;
104
+ const single = master['single'];
105
+ if (single) {
106
+ const singleActions = (single['actions'] ?? []);
107
+ if (!actionExists(singleActions, AXEServiceM8CommandKeys.ProjectSync)) {
108
+ singleActions.push(buildSyncAction());
109
+ }
110
+ single['actions'] = singleActions;
111
+ }
112
+ next['master'] = master;
113
+ return next;
114
+ });
115
+ },
116
+ };
117
+ //#endregion
118
+
119
+ //#region ---- ServiceM8 logo registry key ----
120
+ const axpServiceM8LogoKey = 'servicem8';
121
+ /** Mark artwork (1024×1024); consuming apps copy to `assets/images/brand/integrations/`. */
122
+ const AXE_SERVICEM8_MARK_SRC = 'assets/images/brand/integrations/servicem8-mark.png';
123
+ /** Source mark is square. */
124
+ const AXE_SERVICEM8_MARK_ASPECT = 1;
125
+ //#endregion
126
+
127
+ class AXEServiceM8IntegrationProvider {
128
+ async provide(context) {
129
+ const I18N = `@${RootConfig$1.config.i18n}:integration`;
130
+ context.register({
131
+ domainId: 'project-management',
132
+ id: 'servicem8',
133
+ module: RootConfig$1.module.name,
134
+ displayName: `${I18N}.display-name`,
135
+ description: `${I18N}.description`,
136
+ icon: RootConfig$1.module.icon,
137
+ logoKey: axpServiceM8LogoKey,
138
+ capabilities: [AXP_INTEGRATION_CAPABILITY.Sync, AXP_INTEGRATION_CAPABILITY.Import],
139
+ connectionScopes: [AXP_INTEGRATION_CONNECTION_SCOPE.Tenant],
140
+ allowMultipleConnections: true,
141
+ defaultConnectionKey: 'default',
142
+ connectCommandKey: AXEServiceM8CommandKeys.ConnectionConnect,
143
+ completeOAuthCommandKey: AXEServiceM8CommandKeys.ConnectionCompleteOAuth,
144
+ authMethods: [
145
+ {
146
+ type: AXP_INTEGRATION_AUTH_METHOD_TYPE.OAuth2,
147
+ label: `${I18N}.auth.oauth.label`,
148
+ instructions: `${I18N}.auth.oauth.instructions`,
149
+ usesRedirect: false,
150
+ supportsRefresh: true,
151
+ supportsRevoke: false,
152
+ supportsValidation: true,
153
+ fields: [
154
+ {
155
+ type: AXPWidgetsList.Editors.TextBox,
156
+ path: 'clientId',
157
+ options: { placeholder: `${I18N}.auth.oauth.client-id.placeholder` },
158
+ },
159
+ {
160
+ type: AXPWidgetsList.Editors.PasswordBox,
161
+ path: 'clientSecret',
162
+ options: { placeholder: `${I18N}.auth.oauth.client-secret.placeholder` },
163
+ },
164
+ ],
165
+ },
166
+ ],
167
+ });
168
+ }
169
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEServiceM8IntegrationProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
170
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEServiceM8IntegrationProvider }); }
171
+ }
172
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEServiceM8IntegrationProvider, decorators: [{
173
+ type: Injectable
174
+ }] });
175
+
176
+ //#region ---- ServiceM8 integration settings badges ----
177
+ const SERVICEM8_I18N = '@extensions-servicem8:integration.badges';
178
+ async function formatSyncTimestamp(iso, formatService, settingsService) {
179
+ const parsed = Date.parse(iso);
180
+ if (!Number.isFinite(parsed)) {
181
+ return undefined;
182
+ }
183
+ const dateFormat = await settingsService.get(AXPRegionalSetting.ShortDate);
184
+ const timeFormat = await settingsService.get(AXPRegionalSetting.ShortTime);
185
+ const format = `${dateFormat} ${timeFormat}`.trim();
186
+ return formatService.format(new Date(parsed), 'datetime', { format });
187
+ }
188
+ async function resolveServiceM8ConnectionSettingsBadges(ctx, formatService, settingsService) {
189
+ const config = (ctx.connection.config ?? {});
190
+ const jobsSync = config.jobsSync;
191
+ const lastSync = config.jobsSync?.lastSyncAt?.trim() ||
192
+ (typeof jobsSync?.['lastSuccessfulSyncAt'] === 'string' ? jobsSync['lastSuccessfulSyncAt'].trim() : '');
193
+ if (!lastSync) {
194
+ return [
195
+ {
196
+ labelKey: `${SERVICEM8_I18N}.never-synced`,
197
+ color: 'warning',
198
+ },
199
+ ];
200
+ }
201
+ const datetime = await formatSyncTimestamp(lastSync, formatService, settingsService);
202
+ if (!datetime) {
203
+ return [];
204
+ }
205
+ return [
206
+ {
207
+ labelKey: `${SERVICEM8_I18N}.last-sync`,
208
+ params: { datetime },
209
+ color: 'success',
210
+ },
211
+ ];
212
+ }
213
+ //#endregion
214
+
215
+ class AXEServiceM8IntegrationRuntimeProvider {
216
+ constructor() {
217
+ this.providerId = 'servicem8';
218
+ this.secretStore = inject(AXP_INTEGRATION_SECRET_STORE, { optional: true });
219
+ this.formatService = inject(AXFormatService);
220
+ this.settingsService = inject(AXPSettingsService);
221
+ }
222
+ async getHooks() {
223
+ return {
224
+ validateConnection: (ctx) => this.validateConnection(ctx),
225
+ createClient: (ctx) => this.createClient(ctx),
226
+ getConnectionSettingsBadges: (ctx) => resolveServiceM8ConnectionSettingsBadges(ctx, this.formatService, this.settingsService),
227
+ };
228
+ }
229
+ async validateConnection(ctx) {
230
+ if (!this.hasClientCredentials(ctx)) {
231
+ return {
232
+ valid: false,
233
+ status: AXP_INTEGRATION_CONNECTION_STATUS.Invalid,
234
+ errorMessage: 'ServiceM8 App ID and App Secret are required.',
235
+ };
236
+ }
237
+ return {
238
+ valid: true,
239
+ status: AXP_INTEGRATION_CONNECTION_STATUS.Connected,
240
+ externalAccountName: trim(defaultTo(ctx.instanceUrl, 'ServiceM8')) || 'ServiceM8',
241
+ };
242
+ }
243
+ async createClient(ctx) {
244
+ const credentials = await this.resolveClientCredentials(ctx);
245
+ return {
246
+ clientId: credentials.clientId,
247
+ baseUrl: defaultTo(ctx.instanceUrl, undefined),
248
+ };
249
+ }
250
+ hasClientCredentials(ctx) {
251
+ const config = (ctx.config ?? {});
252
+ const clientId = trim(defaultTo(config.clientId, ''));
253
+ const clientSecret = trim(defaultTo(config.clientSecret, ''));
254
+ const legacyToken = trim(defaultTo(config.accessToken, ''));
255
+ const refreshToken = trim(defaultTo(config.refreshToken, ''));
256
+ return Boolean((clientId && clientSecret) || legacyToken || refreshToken);
257
+ }
258
+ async resolveClientCredentials(ctx) {
259
+ const config = (ctx.config ?? {});
260
+ const configClientId = trim(defaultTo(config.clientId, ''));
261
+ const configClientSecret = trim(defaultTo(config.clientSecret, ''));
262
+ if (configClientId && configClientSecret) {
263
+ return { clientId: configClientId, clientSecret: configClientSecret };
264
+ }
265
+ if (!this.secretStore || !trim(defaultTo(ctx.secretRef, ''))) {
266
+ throw new Error('ServiceM8 connection has no client credentials.');
267
+ }
268
+ const credentials = await this.secretStore.resolve(ctx.secretRef, ctx);
269
+ const clientId = trim(defaultTo(credentials.token ?? credentials.email, ''));
270
+ const clientSecret = trim(defaultTo(credentials.password ?? credentials.apiToken, ''));
271
+ if (!clientId || !clientSecret) {
272
+ throw new Error('ServiceM8 connection has no usable client credentials.');
273
+ }
274
+ return { clientId, clientSecret };
275
+ }
276
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEServiceM8IntegrationRuntimeProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
277
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEServiceM8IntegrationRuntimeProvider }); }
278
+ }
279
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEServiceM8IntegrationRuntimeProvider, decorators: [{
280
+ type: Injectable
281
+ }] });
282
+ const provideServiceM8IntegrationRuntimeProvider = {
283
+ provide: AXP_INTEGRATION_RUNTIME_PROVIDER,
284
+ useClass: AXEServiceM8IntegrationRuntimeProvider,
285
+ multi: true,
286
+ };
287
+
288
+ const servicem8LogoProvider = {
289
+ key: axpServiceM8LogoKey,
290
+ resolve: async (request) => ({
291
+ kind: 'image',
292
+ src: AXE_SERVICEM8_MARK_SRC,
293
+ height: request.size,
294
+ width: Math.round(request.size * AXE_SERVICEM8_MARK_ASPECT),
295
+ }),
296
+ };
297
+ const AXE_SERVICEM8_LOGO_PROVIDER = {
298
+ provide: AXP_LOGO_PROVIDER,
299
+ useValue: servicem8LogoProvider,
300
+ multi: true,
301
+ };
302
+
303
+ //#region ---- Manifest ----
304
+ const ServiceM8ExtensionManifest = {
305
+ name: RootConfig$1.module.name,
306
+ version: '1.0.0',
307
+ title: RootConfig$1.module.title,
308
+ icon: RootConfig$1.module.icon,
309
+ i18n: RootConfig$1.config.i18n,
310
+ type: 'integration',
311
+ domain: 'project-management',
312
+ dependencies: [],
313
+ };
314
+ //#endregion
315
+
316
+ //#region ---- Imports ----
317
+ //#endregion
318
+ class AXEServiceM8MenuProvider {
319
+ async provide(context) {
320
+ const module = RootConfig$1.module;
321
+ const policy = {
322
+ features: [module.name],
323
+ permissions: [AXPAdministrationPermissionKeys.MaintainConnections],
324
+ };
325
+ const configMenu = context.find(AXPCommonMenuKeys.Configuration);
326
+ if (!context.find(AXEServiceM8MenuKeys.ServiceM8ExtensionConfig).exists) {
327
+ configMenu.addItems([
328
+ {
329
+ name: AXEServiceM8MenuKeys.ServiceM8ExtensionConfig,
330
+ text: '@extensions-servicem8:module.menus.configuration.title',
331
+ icon: module.icon,
332
+ type: 'menu',
333
+ priority: 8940,
334
+ policy,
335
+ children: [],
336
+ },
337
+ ]);
338
+ }
339
+ }
340
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEServiceM8MenuProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
341
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEServiceM8MenuProvider }); }
342
+ }
343
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEServiceM8MenuProvider, decorators: [{
344
+ type: Injectable
345
+ }] });
346
+
347
+ //#region ---- Imports ----
348
+ //#endregion
349
+ class AXEServiceM8ExtensionModule {
350
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEServiceM8ExtensionModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
351
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: AXEServiceM8ExtensionModule, imports: [AXMIntegrationManagementModule] }); }
352
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEServiceM8ExtensionModule, providers: [
353
+ AXE_SERVICEM8_LOGO_PROVIDER,
354
+ { provide: AXP_MODULE_MANIFEST_PROVIDER, useValue: ServiceM8ExtensionManifest, multi: true },
355
+ { provide: AXP_MENU_PROVIDER, useClass: AXEServiceM8MenuProvider, multi: true },
356
+ { provide: AXP_ENTITY_MODIFIER, useValue: AXEServiceM8ProjectEntityModifier, multi: true },
357
+ {
358
+ provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
359
+ useClass: AXEServiceM8ProjectLinkExpressionEvaluatorScopeProvider,
360
+ multi: true,
361
+ },
362
+ { provide: AXP_INTEGRATION_PROVIDER, useClass: AXEServiceM8IntegrationProvider, multi: true },
363
+ provideServiceM8IntegrationRuntimeProvider,
364
+ provideCommandSetups([
365
+ {
366
+ key: AXEServiceM8CommandKeys.ConnectionConnect,
367
+ command: () => import('./acorex-platform-extension-project-management-servicem8-client-connect-servicem8.command-CkDOwDrg.mjs').then((c) => c.ConnectServiceM8Command),
368
+ },
369
+ {
370
+ key: AXEServiceM8CommandKeys.ConnectionCompleteOAuth,
371
+ command: () => import('./acorex-platform-extension-project-management-servicem8-client-complete-servicem8-oauth.command-CepIVvQD.mjs').then((c) => c.CompleteServiceM8OAuthCommand),
372
+ },
373
+ ]),
374
+ ], imports: [AXMIntegrationManagementModule] }); }
375
+ }
376
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEServiceM8ExtensionModule, decorators: [{
377
+ type: NgModule,
378
+ args: [{
379
+ imports: [AXMIntegrationManagementModule],
380
+ providers: [
381
+ AXE_SERVICEM8_LOGO_PROVIDER,
382
+ { provide: AXP_MODULE_MANIFEST_PROVIDER, useValue: ServiceM8ExtensionManifest, multi: true },
383
+ { provide: AXP_MENU_PROVIDER, useClass: AXEServiceM8MenuProvider, multi: true },
384
+ { provide: AXP_ENTITY_MODIFIER, useValue: AXEServiceM8ProjectEntityModifier, multi: true },
385
+ {
386
+ provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
387
+ useClass: AXEServiceM8ProjectLinkExpressionEvaluatorScopeProvider,
388
+ multi: true,
389
+ },
390
+ { provide: AXP_INTEGRATION_PROVIDER, useClass: AXEServiceM8IntegrationProvider, multi: true },
391
+ provideServiceM8IntegrationRuntimeProvider,
392
+ provideCommandSetups([
393
+ {
394
+ key: AXEServiceM8CommandKeys.ConnectionConnect,
395
+ command: () => import('./acorex-platform-extension-project-management-servicem8-client-connect-servicem8.command-CkDOwDrg.mjs').then((c) => c.ConnectServiceM8Command),
396
+ },
397
+ {
398
+ key: AXEServiceM8CommandKeys.ConnectionCompleteOAuth,
399
+ command: () => import('./acorex-platform-extension-project-management-servicem8-client-complete-servicem8-oauth.command-CepIVvQD.mjs').then((c) => c.CompleteServiceM8OAuthCommand),
400
+ },
401
+ ]),
402
+ ],
403
+ }]
404
+ }] });
405
+
406
+ /**
407
+ * Generated bundle index. Do not edit.
408
+ */
409
+
410
+ export { AXEServiceM8ExtensionModule, AXE_SERVICEM8_LOGO_PROVIDER, AXE_SERVICEM8_MARK_ASPECT, AXE_SERVICEM8_MARK_SRC, axpServiceM8LogoKey };
411
+ //# sourceMappingURL=acorex-platform-extension-project-management-servicem8-client.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acorex-platform-extension-project-management-servicem8-client.mjs","sources":["../../../../libs/extensions/project-management/servicem8/client/src/lib/features/project-management/servicem8-expression-scope.ts","../../../../libs/extensions/project-management/servicem8/client/src/lib/features/project-management/servicem8-project-link-expression-evaluator-scope.provider.ts","../../../../libs/extensions/project-management/servicem8/client/src/lib/features/project-management/project-servicem8.entity-modifier.ts","../../../../libs/extensions/project-management/servicem8/client/src/lib/brand/servicem8-logo-key.ts","../../../../libs/extensions/project-management/servicem8/client/src/lib/integration/servicem8.integration.provider.ts","../../../../libs/extensions/project-management/servicem8/client/src/lib/integration/servicem8-integration-settings-badges.util.ts","../../../../libs/extensions/project-management/servicem8/client/src/lib/integration/servicem8.integration-runtime.provider.ts","../../../../libs/extensions/project-management/servicem8/client/src/lib/brand/servicem8-logo.provider.ts","../../../../libs/extensions/project-management/servicem8/client/src/lib/manifest.ts","../../../../libs/extensions/project-management/servicem8/client/src/lib/menu.provider.ts","../../../../libs/extensions/project-management/servicem8/client/src/lib/servicem8-extension.module.ts","../../../../libs/extensions/project-management/servicem8/client/src/acorex-platform-extension-project-management-servicem8-client.ts"],"sourcesContent":["export const AXE_SERVICEM8_EXPRESSION_SCOPE = 'ServiceM8Extension';\n","//#region ---- Imports ----\n\nimport { AXPExpressionEvaluatorScopeProvider, AXPExpressionEvaluatorScopeProviderContext } from '@acorex-platform/framework-shared/core';\nimport { AXPIntegrationRuntimeService } from '@acorex-platform/framework-client/common';\nimport { AXPEntityService } from '@acorex-platform/framework-client/layout/entity';\nimport { inject } from '@angular/core';\nimport { AXE_PM_INTEGRATION_PROVIDER_SERVICEM8 } from '@acorex-platform/extension-project-management-core-client/contracts';\nimport { RootConfig as PmRootConfig } from '@acorex-platform/module-project-management-client/contracts';\nimport type { AXMProjectManagementProjectEntityModel } from '@acorex-platform/module-project-management-client/contracts';\nimport { AXE_SERVICEM8_EXPRESSION_SCOPE } from './servicem8-expression-scope';\n\n//#endregion\n\nexport class AXEServiceM8ProjectLinkExpressionEvaluatorScopeProvider implements AXPExpressionEvaluatorScopeProvider {\n private readonly entityService = inject(AXPEntityService);\n private readonly integrationRuntime = inject(AXPIntegrationRuntimeService);\n\n async provide(context: AXPExpressionEvaluatorScopeProviderContext): Promise<void> {\n context.addScope(AXE_SERVICEM8_EXPRESSION_SCOPE, {\n hasProjectLink: async (platformProjectIdRaw?: unknown): Promise<boolean> => {\n const platformProjectId = typeof platformProjectIdRaw === 'string' ? platformProjectIdRaw.trim() : '';\n if (!platformProjectId) {\n return false;\n }\n\n const project = (await this.entityService\n .withEntity(PmRootConfig.module.name, PmRootConfig.entities.project.name)\n .data<string, AXMProjectManagementProjectEntityModel>()\n .byKey(platformProjectId)) as AXMProjectManagementProjectEntityModel | null;\n\n return project?.integrationRef?.source === AXE_PM_INTEGRATION_PROVIDER_SERVICEM8;\n },\n hasServiceM8Connection: async (): Promise<boolean> => {\n try {\n await this.integrationRuntime.getConnection({ providerId: 'servicem8', connectionScope: 'tenant' });\n return true;\n } catch {\n return false;\n }\n },\n });\n }\n}\n\n//#endregion\n","//#region ---- Imports ----\n\nimport { AXPSystemActionType } from '@acorex-platform/framework-shared/core';\nimport { AXPEntityAction, AXPEntityCommandScope } from '@acorex-platform/framework-shared/entity';\nimport {\n actionExists,\n AXPEntityModifierProvider,\n ensureListActions,\n} from '@acorex-platform/framework-client/layout/entity';\nimport { AXEServiceM8CommandKeys } from '@acorex-platform/extension-project-management-servicem8-client/contracts';\nimport { RootConfig as PmRootConfig } from '@acorex-platform/module-project-management-client/contracts';\nimport { AXE_SERVICEM8_EXPRESSION_SCOPE } from './servicem8-expression-scope';\n\n//#endregion\n\nconst IMPORT_ORDER = 36;\nconst SYNC_ORDER = 37;\n\nfunction buildImportAction(): AXPEntityAction {\n return {\n name: AXEServiceM8CommandKeys.ProjectImportFromServiceM8,\n title: '@extensions-servicem8:actions.import-from-servicem8.title',\n command: { name: AXEServiceM8CommandKeys.ProjectImportFromServiceM8 },\n priority: 'secondary',\n type: AXPSystemActionType.Import,\n icon: 'fa-light fa-briefcase',\n scope: AXPEntityCommandScope.TypeLevel,\n order: IMPORT_ORDER,\n };\n}\n\nfunction buildSyncAction(): AXPEntityAction {\n return {\n name: AXEServiceM8CommandKeys.ProjectSync,\n title: '@extensions-servicem8:actions.sync.title',\n command: {\n name: AXEServiceM8CommandKeys.ProjectSync,\n options: { platformProjectId: '{{ context.eval(\"id\") }}' },\n },\n priority: 'secondary',\n type: AXPSystemActionType.Sync,\n icon: 'fa-light fa-arrows-rotate',\n scope: AXPEntityCommandScope.Individual,\n order: SYNC_ORDER,\n hidden: `{{ !(await ${AXE_SERVICEM8_EXPRESSION_SCOPE}.hasProjectLink(context.eval(\"id\"))) }}`,\n };\n}\n\n/** Adds ServiceM8 import/sync actions to ProjectManagement.Project views. */\nexport const AXEServiceM8ProjectEntityModifier: AXPEntityModifierProvider = {\n entityName: PmRootConfig.entities.project.name,\n order: 50,\n modifier: (ctx) => {\n if (ctx.module.get() !== PmRootConfig.module.name) {\n return;\n }\n\n ensureListActions(ctx);\n\n ctx.interfaces.update((interfaces) => {\n const next = (interfaces ?? {}) as Record<string, unknown>;\n const master = (next['master'] ?? {}) as Record<string, unknown>;\n const list = (master['list'] ?? { actions: [], views: [] }) as Record<string, unknown>;\n const listActions = (list['actions'] ?? []) as AXPEntityAction[];\n\n if (!actionExists(listActions, AXEServiceM8CommandKeys.ProjectImportFromServiceM8)) {\n listActions.push(buildImportAction());\n }\n list['actions'] = listActions;\n master['list'] = list;\n\n const single = master['single'] as Record<string, unknown> | undefined;\n if (single) {\n const singleActions = (single['actions'] ?? []) as AXPEntityAction[];\n if (!actionExists(singleActions, AXEServiceM8CommandKeys.ProjectSync)) {\n singleActions.push(buildSyncAction());\n }\n single['actions'] = singleActions;\n }\n\n next['master'] = master;\n return next as typeof interfaces;\n });\n },\n};\n\n//#endregion\n","//#region ---- ServiceM8 logo registry key ----\n\nexport const axpServiceM8LogoKey = 'servicem8' as const;\n\n/** Mark artwork (1024×1024); consuming apps copy to `assets/images/brand/integrations/`. */\nexport const AXE_SERVICEM8_MARK_SRC = 'assets/images/brand/integrations/servicem8-mark.png';\n\n/** Source mark is square. */\nexport const AXE_SERVICEM8_MARK_ASPECT = 1;\n\n//#endregion\n","import {\n AXP_INTEGRATION_AUTH_METHOD_TYPE,\n AXP_INTEGRATION_CAPABILITY,\n AXP_INTEGRATION_CONNECTION_SCOPE,\n AXPWidgetsList,\n} from '@acorex-platform/framework-shared/core';\nimport { AXPIntegrationProvider, AXPIntegrationProviderContext } from '@acorex-platform/framework-client/common';\nimport { Injectable } from '@angular/core';\nimport { AXEServiceM8CommandKeys, RootConfig } from '@acorex-platform/extension-project-management-servicem8-client/contracts';\nimport { axpServiceM8LogoKey } from '../brand/servicem8-logo-key';\n\n@Injectable()\nexport class AXEServiceM8IntegrationProvider implements AXPIntegrationProvider {\n async provide(context: AXPIntegrationProviderContext): Promise<void> {\n const I18N = `@${RootConfig.config.i18n}:integration`;\n\n context.register({\n domainId: 'project-management',\n id: 'servicem8',\n module: RootConfig.module.name,\n displayName: `${I18N}.display-name`,\n description: `${I18N}.description`,\n icon: RootConfig.module.icon,\n logoKey: axpServiceM8LogoKey,\n capabilities: [AXP_INTEGRATION_CAPABILITY.Sync, AXP_INTEGRATION_CAPABILITY.Import],\n connectionScopes: [AXP_INTEGRATION_CONNECTION_SCOPE.Tenant],\n allowMultipleConnections: true,\n defaultConnectionKey: 'default',\n connectCommandKey: AXEServiceM8CommandKeys.ConnectionConnect,\n completeOAuthCommandKey: AXEServiceM8CommandKeys.ConnectionCompleteOAuth,\n authMethods: [\n {\n type: AXP_INTEGRATION_AUTH_METHOD_TYPE.OAuth2,\n label: `${I18N}.auth.oauth.label`,\n instructions: `${I18N}.auth.oauth.instructions`,\n usesRedirect: false,\n supportsRefresh: true,\n supportsRevoke: false,\n supportsValidation: true,\n fields: [\n {\n type: AXPWidgetsList.Editors.TextBox,\n path: 'clientId',\n options: { placeholder: `${I18N}.auth.oauth.client-id.placeholder` },\n },\n {\n type: AXPWidgetsList.Editors.PasswordBox,\n path: 'clientSecret',\n options: { placeholder: `${I18N}.auth.oauth.client-secret.placeholder` },\n },\n ],\n },\n ],\n });\n }\n}\n","//#region ---- ServiceM8 integration settings badges ----\n\nimport { AXFormatService } from '@acorex/core/format';\nimport { AXPSettingsService } from '@acorex-platform/framework-client/common';\nimport { AXPRegionalSetting } from '@acorex-platform/framework-shared/core';\nimport type {\n AXPIntegrationConnectionSettingsContext,\n AXPIntegrationSettingsRowBadge,\n} from '@acorex-platform/framework-shared/core';\nimport type { AXEServiceM8ConnectionConfig } from '@acorex-platform/extension-project-management-servicem8-client/contracts';\n\nconst SERVICEM8_I18N = '@extensions-servicem8:integration.badges';\n\nasync function formatSyncTimestamp(\n iso: string,\n formatService: AXFormatService,\n settingsService: AXPSettingsService,\n): Promise<string | undefined> {\n const parsed = Date.parse(iso);\n if (!Number.isFinite(parsed)) {\n return undefined;\n }\n\n const dateFormat = await settingsService.get(AXPRegionalSetting.ShortDate);\n const timeFormat = await settingsService.get(AXPRegionalSetting.ShortTime);\n const format = `${dateFormat} ${timeFormat}`.trim();\n\n return formatService.format(new Date(parsed), 'datetime', { format });\n}\n\nexport async function resolveServiceM8ConnectionSettingsBadges(\n ctx: AXPIntegrationConnectionSettingsContext,\n formatService: AXFormatService,\n settingsService: AXPSettingsService,\n): Promise<AXPIntegrationSettingsRowBadge[]> {\n const config = (ctx.connection.config ?? {}) as AXEServiceM8ConnectionConfig;\n const jobsSync = config.jobsSync as Record<string, unknown> | undefined;\n const lastSync =\n config.jobsSync?.lastSyncAt?.trim() ||\n (typeof jobsSync?.['lastSuccessfulSyncAt'] === 'string' ? jobsSync['lastSuccessfulSyncAt'].trim() : '');\n\n if (!lastSync) {\n return [\n {\n labelKey: `${SERVICEM8_I18N}.never-synced`,\n color: 'warning',\n },\n ];\n }\n\n const datetime = await formatSyncTimestamp(lastSync, formatService, settingsService);\n if (!datetime) {\n return [];\n }\n\n return [\n {\n labelKey: `${SERVICEM8_I18N}.last-sync`,\n params: { datetime },\n color: 'success',\n },\n ];\n}\n\n//#endregion\n","import {\n AXP_INTEGRATION_CONNECTION_STATUS,\n type AXPIntegrationProviderRuntimeHooks,\n type AXPIntegrationRuntimeContext,\n type AXPIntegrationValidationResult,\n} from '@acorex-platform/framework-shared/core';\nimport {\n AXP_INTEGRATION_RUNTIME_PROVIDER,\n AXP_INTEGRATION_SECRET_STORE,\n type AXPIntegrationRuntimeProvider,\n} from '@acorex-platform/framework-client/common';\nimport type { AXEServiceM8ConnectionConfig } from '@acorex-platform/extension-project-management-servicem8-client/contracts';\nimport { Injectable, inject } from '@angular/core';\nimport { AXFormatService } from '@acorex/core/format';\nimport { AXPSettingsService } from '@acorex-platform/framework-client/common';\nimport { defaultTo, trim } from 'lodash-es';\nimport { resolveServiceM8ConnectionSettingsBadges } from './servicem8-integration-settings-badges.util';\n\n@Injectable()\nexport class AXEServiceM8IntegrationRuntimeProvider implements AXPIntegrationRuntimeProvider {\n readonly providerId = 'servicem8';\n\n private readonly secretStore = inject(AXP_INTEGRATION_SECRET_STORE, { optional: true });\n private readonly formatService = inject(AXFormatService);\n private readonly settingsService = inject(AXPSettingsService);\n\n async getHooks(): Promise<AXPIntegrationProviderRuntimeHooks> {\n return {\n validateConnection: (ctx) => this.validateConnection(ctx),\n createClient: (ctx) => this.createClient(ctx),\n getConnectionSettingsBadges: (ctx) =>\n resolveServiceM8ConnectionSettingsBadges(ctx, this.formatService, this.settingsService),\n };\n }\n\n private async validateConnection(ctx: AXPIntegrationRuntimeContext): Promise<AXPIntegrationValidationResult> {\n if (!this.hasClientCredentials(ctx)) {\n return {\n valid: false,\n status: AXP_INTEGRATION_CONNECTION_STATUS.Invalid,\n errorMessage: 'ServiceM8 App ID and App Secret are required.',\n };\n }\n\n return {\n valid: true,\n status: AXP_INTEGRATION_CONNECTION_STATUS.Connected,\n externalAccountName: trim(defaultTo(ctx.instanceUrl, 'ServiceM8')) || 'ServiceM8',\n };\n }\n\n private async createClient(ctx: AXPIntegrationRuntimeContext): Promise<{ clientId: string; baseUrl?: string }> {\n const credentials = await this.resolveClientCredentials(ctx);\n return {\n clientId: credentials.clientId,\n baseUrl: defaultTo(ctx.instanceUrl, undefined),\n };\n }\n\n private hasClientCredentials(ctx: AXPIntegrationRuntimeContext): boolean {\n const config = (ctx.config ?? {}) as AXEServiceM8ConnectionConfig;\n const clientId = trim(defaultTo(config.clientId, ''));\n const clientSecret = trim(defaultTo(config.clientSecret, ''));\n const legacyToken = trim(defaultTo(config.accessToken, ''));\n const refreshToken = trim(defaultTo(config.refreshToken, ''));\n return Boolean((clientId && clientSecret) || legacyToken || refreshToken);\n }\n\n private async resolveClientCredentials(\n ctx: AXPIntegrationRuntimeContext,\n ): Promise<{ clientId: string; clientSecret?: string }> {\n const config = (ctx.config ?? {}) as AXEServiceM8ConnectionConfig;\n const configClientId = trim(defaultTo(config.clientId, ''));\n const configClientSecret = trim(defaultTo(config.clientSecret, ''));\n\n if (configClientId && configClientSecret) {\n return { clientId: configClientId, clientSecret: configClientSecret };\n }\n\n if (!this.secretStore || !trim(defaultTo(ctx.secretRef, ''))) {\n throw new Error('ServiceM8 connection has no client credentials.');\n }\n\n const credentials = await this.secretStore.resolve(ctx.secretRef, ctx);\n const clientId = trim(defaultTo(credentials.token ?? credentials.email, ''));\n const clientSecret = trim(defaultTo(credentials.password ?? credentials.apiToken, ''));\n if (!clientId || !clientSecret) {\n throw new Error('ServiceM8 connection has no usable client credentials.');\n }\n\n return { clientId, clientSecret };\n }\n}\n\nexport const provideServiceM8IntegrationRuntimeProvider = {\n provide: AXP_INTEGRATION_RUNTIME_PROVIDER,\n useClass: AXEServiceM8IntegrationRuntimeProvider,\n multi: true,\n};\n","import {\n AXP_LOGO_PROVIDER,\n type AXPLogoProvider,\n type AXPLogoRequest,\n type AXPLogoView,\n} from '@acorex-platform/framework-client/core';\nimport {\n AXE_SERVICEM8_MARK_ASPECT,\n AXE_SERVICEM8_MARK_SRC,\n axpServiceM8LogoKey,\n} from './servicem8-logo-key';\n\nconst servicem8LogoProvider: AXPLogoProvider = {\n key: axpServiceM8LogoKey,\n resolve: async (request: AXPLogoRequest): Promise<AXPLogoView> => ({\n kind: 'image',\n src: AXE_SERVICEM8_MARK_SRC,\n height: request.size,\n width: Math.round(request.size * AXE_SERVICEM8_MARK_ASPECT),\n }),\n};\n\nexport const AXE_SERVICEM8_LOGO_PROVIDER = {\n provide: AXP_LOGO_PROVIDER,\n useValue: servicem8LogoProvider,\n multi: true,\n} as const;\n","//#region ---- Manifest ----\n\nimport { AXPModuleManifest } from '@acorex-platform/framework-client/core';\nimport { RootConfig } from '@acorex-platform/extension-project-management-servicem8-client/contracts';\n\nexport const ServiceM8ExtensionManifest: AXPModuleManifest = {\n name: RootConfig.module.name,\n version: '1.0.0',\n title: RootConfig.module.title,\n icon: RootConfig.module.icon,\n i18n: RootConfig.config.i18n,\n type: 'integration',\n domain: 'project-management',\n dependencies: [],\n};\n\n//#endregion\n","//#region ---- Imports ----\n\nimport {\n AXPCommonMenuKeys\n} from '@acorex-platform/module-common-client/contracts';\nimport { AXPMenuProvider, AXPMenuProviderContext } from '@acorex-platform/framework-client/common';\nimport { Injectable } from '@angular/core';\nimport {\n AXEServiceM8MenuKeys,\n RootConfig,\n AXPAdministrationPermissionKeys\n} from '@acorex-platform/extension-project-management-servicem8-client/contracts';\n\n//#endregion\n\n@Injectable()\nexport class AXEServiceM8MenuProvider implements AXPMenuProvider {\n async provide(context: AXPMenuProviderContext): Promise<void> {\n const module = RootConfig.module;\n const policy = {\n features: [module.name],\n permissions: [AXPAdministrationPermissionKeys.MaintainConnections],\n };\n\n const configMenu = context.find(AXPCommonMenuKeys.Configuration);\n if (!context.find(AXEServiceM8MenuKeys.ServiceM8ExtensionConfig).exists) {\n configMenu.addItems([\n {\n name: AXEServiceM8MenuKeys.ServiceM8ExtensionConfig,\n text: '@extensions-servicem8:module.menus.configuration.title',\n icon: module.icon,\n type: 'menu',\n priority: 8940,\n policy,\n children: [],\n },\n ]);\n }\n }\n}\n\n//#endregion\n","//#region ---- Imports ----\n\nimport { AXMIntegrationManagementModule } from '@acorex-platform/module-integration-management-client';\nimport { AXP_INTEGRATION_PROVIDER, AXP_MENU_PROVIDER } from '@acorex-platform/framework-client/common';\nimport { AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER, AXP_MODULE_MANIFEST_PROVIDER } from '@acorex-platform/framework-client/core';\nimport { AXP_ENTITY_MODIFIER } from '@acorex-platform/framework-client/layout/entity';\nimport { provideCommandSetups } from '@acorex-platform/framework-client/runtime';\nimport { NgModule } from '@angular/core';\nimport { AXEServiceM8CommandKeys } from '@acorex-platform/extension-project-management-servicem8-client/contracts';\nimport { AXEServiceM8ProjectLinkExpressionEvaluatorScopeProvider } from './features/project-management/servicem8-project-link-expression-evaluator-scope.provider';\nimport { AXEServiceM8ProjectEntityModifier } from './features/project-management/project-servicem8.entity-modifier';\nimport { AXEServiceM8IntegrationProvider } from './integration/servicem8.integration.provider';\nimport { provideServiceM8IntegrationRuntimeProvider } from './integration/servicem8.integration-runtime.provider';\nimport { AXE_SERVICEM8_LOGO_PROVIDER } from './brand/servicem8-logo.provider';\nimport { ServiceM8ExtensionManifest } from './manifest';\nimport { AXEServiceM8MenuProvider } from './menu.provider';\n\n//#endregion\n\n@NgModule({\n imports: [AXMIntegrationManagementModule],\n providers: [\n AXE_SERVICEM8_LOGO_PROVIDER,\n { provide: AXP_MODULE_MANIFEST_PROVIDER, useValue: ServiceM8ExtensionManifest, multi: true },\n { provide: AXP_MENU_PROVIDER, useClass: AXEServiceM8MenuProvider, multi: true },\n { provide: AXP_ENTITY_MODIFIER, useValue: AXEServiceM8ProjectEntityModifier, multi: true },\n {\n provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,\n useClass: AXEServiceM8ProjectLinkExpressionEvaluatorScopeProvider,\n multi: true,\n },\n { provide: AXP_INTEGRATION_PROVIDER, useClass: AXEServiceM8IntegrationProvider, multi: true },\n provideServiceM8IntegrationRuntimeProvider,\n provideCommandSetups([\n {\n key: AXEServiceM8CommandKeys.ConnectionConnect,\n command: () => import('./features/connect/connect-servicem8.command').then((c) => c.ConnectServiceM8Command),\n },\n {\n key: AXEServiceM8CommandKeys.ConnectionCompleteOAuth,\n command: () =>\n import('./features/connect/complete-servicem8-oauth.command').then((c) => c.CompleteServiceM8OAuthCommand),\n },\n ]),\n ],\n})\nexport class AXEServiceM8ExtensionModule {}\n\n//#endregion\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["PmRootConfig","RootConfig"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@acorex-platform/extension-project-management-servicem8-client",
3
+ "version": "0.0.21",
4
+ "sideEffects": false,
5
+ "peerDependencies": {
6
+ "@angular/common": "^21.0.0",
7
+ "@angular/core": "^21.0.0"
8
+ },
9
+ "dependencies": {
10
+ "tslib": "^2.3.0"
11
+ },
12
+ "module": "fesm2022/acorex-platform-extension-project-management-servicem8-client.mjs",
13
+ "typings": "types/acorex-platform-extension-project-management-servicem8-client.d.ts",
14
+ "exports": {
15
+ "./package.json": {
16
+ "default": "./package.json"
17
+ },
18
+ ".": {
19
+ "types": "./types/acorex-platform-extension-project-management-servicem8-client.d.ts",
20
+ "default": "./fesm2022/acorex-platform-extension-project-management-servicem8-client.mjs"
21
+ },
22
+ "./contracts": {
23
+ "types": "./types/acorex-platform-extension-project-management-servicem8-client-contracts.d.ts",
24
+ "default": "./fesm2022/acorex-platform-extension-project-management-servicem8-client-contracts.mjs"
25
+ }
26
+ },
27
+ "type": "module"
28
+ }
@@ -0,0 +1,184 @@
1
+ import { AXEPMProjectSyncStrategy } from '@acorex-platform/extension-project-management-core-contracts';
2
+ export { AXEPMProjectSyncStrategy, AXEPMProjectSyncStrategy as AXEServiceM8ProjectSyncStrategy, AXE_PM_PROJECT_SYNC_STRATEGIES as AXE_SERVICEM8_PROJECT_SYNC_STRATEGIES } from '@acorex-platform/extension-project-management-core-contracts';
3
+
4
+ declare const RootConfig: {
5
+ config: {
6
+ i18n: string;
7
+ };
8
+ module: {
9
+ name: string;
10
+ title: string;
11
+ icon: string;
12
+ };
13
+ };
14
+
15
+ declare const AXEServiceM8CommandKeys: {
16
+ readonly ConnectionConnect: "servicem8-extension.Connection:Connect";
17
+ readonly ConnectionCompleteOAuth: "servicem8-extension.Connection:CompleteOAuth";
18
+ readonly OAuthStart: "servicem8-extension.OAuth:Start";
19
+ readonly OAuthComplete: "servicem8-extension.OAuth:Complete";
20
+ readonly OAuthValidate: "servicem8-extension.OAuth:Validate";
21
+ readonly ProjectImportFromServiceM8: "servicem8-extension.Project:ImportFromServiceM8";
22
+ readonly ProjectSync: "servicem8-extension.Project:Sync";
23
+ readonly JobsPull: "servicem8-extension.Jobs:Pull";
24
+ readonly JobsRefresh: "servicem8-extension.Jobs:Refresh";
25
+ };
26
+
27
+ declare const AXEServiceM8MenuKeys: {
28
+ readonly ServiceM8ExtensionConfig: "servicem8-extension:Menu:Configuration";
29
+ };
30
+
31
+ /**
32
+ * Administration capabilities used by this extension.
33
+ * Domain capability keys for this module. Root groups live in `AXPCommonPermissionGroupKeys` (common contracts).
34
+ */
35
+ declare const AXPAdministrationPermissionKeys: {
36
+ readonly Access: "Administration:Permission:Access";
37
+ readonly MaintainUsersAndRoles: "Administration:Permission:MaintainUsersAndRoles";
38
+ readonly ManageSessions: "Administration:Permission:ManageSessions";
39
+ readonly MaintainPolicies: "Administration:Permission:MaintainPolicies";
40
+ readonly ViewIntegrations: "Administration:Permission:ViewIntegrations";
41
+ readonly MaintainConnections: "Administration:Permission:MaintainConnections";
42
+ readonly ManageIntegrationActions: "Administration:Permission:ManageIntegrationActions";
43
+ readonly MaintainWorkflows: "Administration:Permission:MaintainWorkflows";
44
+ readonly ManageWorkflowActions: "Administration:Permission:ManageWorkflowActions";
45
+ readonly ViewRules: "Administration:Permission:ViewRules";
46
+ readonly MaintainRules: "Administration:Permission:MaintainRules";
47
+ readonly ManageRuleActions: "Administration:Permission:ManageRuleActions";
48
+ readonly MaintainAi: "Administration:Permission:MaintainAi";
49
+ readonly UseAiAssist: "Administration:Permission:UseAiAssist";
50
+ readonly MaintainDashboards: "Administration:Permission:MaintainDashboards";
51
+ readonly ViewJobs: "Administration:Permission:ViewJobs";
52
+ readonly ManageJobActions: "Administration:Permission:ManageJobActions";
53
+ readonly ViewObservability: "Administration:Permission:ViewObservability";
54
+ readonly MaintainObservability: "Administration:Permission:MaintainObservability";
55
+ };
56
+ /**
57
+ * @deprecated Prefer {@link AXPAdministrationPermissionKeys}.
58
+ * Legacy nested shape kept so existing imports compile during migration.
59
+ */
60
+ declare const AXEServiceM8Permissions: {
61
+ readonly ServiceM8Extension: {
62
+ readonly Management: "Administration:Permission:MaintainConnections";
63
+ readonly Catalog: {
64
+ readonly View: "Administration:Permission:ViewIntegrations";
65
+ };
66
+ readonly ProjectImport: {
67
+ readonly Execute: "Administration:Permission:ManageIntegrationActions";
68
+ };
69
+ readonly ProjectSync: {
70
+ readonly Execute: "Administration:Permission:ManageIntegrationActions";
71
+ };
72
+ };
73
+ };
74
+
75
+ /** Stored on IntegrationManagement connection `config` for tenant-scoped ServiceM8. */
76
+ interface AXEServiceM8ConnectionConfig {
77
+ /** OAuth2 App ID from the ServiceM8 developer add-on. */
78
+ clientId?: string;
79
+ /** OAuth2 App Secret from the ServiceM8 developer add-on. */
80
+ clientSecret?: string;
81
+ /** Space-separated scopes granted at authorize time. */
82
+ scope?: string;
83
+ /** Current bearer token (authorization-code flow). */
84
+ accessToken?: string;
85
+ /** Refresh token from ServiceM8 token response. */
86
+ refreshToken?: string;
87
+ /** ISO timestamp when `accessToken` expires. */
88
+ tokenExpiresAt?: string;
89
+ /** Full ServiceM8 token JSON response. */
90
+ oauthTokenJson?: string;
91
+ /** Background job pull sync state (server-managed). */
92
+ jobsSync?: AXEServiceM8JobsSyncState;
93
+ }
94
+ /** ServiceM8 job pull sync state stored on the tenant connection `config.jobsSync`. */
95
+ interface AXEServiceM8JobsSyncState {
96
+ /** ISO timestamp — last successful job pull (incremental sync watermark). */
97
+ lastSyncAt?: string;
98
+ /** ISO timestamp — last full snapshot reconcile (deactivation pass). */
99
+ lastFullReconcileAt?: string;
100
+ }
101
+
102
+ interface AXEServiceM8OAuthStartRequest {
103
+ redirectUri: string;
104
+ connectionId?: string;
105
+ }
106
+ interface AXEServiceM8OAuthStartResponse {
107
+ redirectUrl: string;
108
+ state: string;
109
+ }
110
+ interface AXEServiceM8OAuthCompleteRequest {
111
+ state: string;
112
+ code: string;
113
+ }
114
+ interface AXEServiceM8OAuthCompleteResponse {
115
+ connectionId: string;
116
+ }
117
+ interface AXEServiceM8OAuthValidateRequest {
118
+ connectionId?: string;
119
+ }
120
+ interface AXEServiceM8OAuthValidateResponse {
121
+ valid: boolean;
122
+ message?: string;
123
+ }
124
+
125
+ /** Subset of ServiceM8 `job.json` fields used for cache / import / sync. */
126
+ interface AXEServiceM8RemoteJob {
127
+ uuid: string;
128
+ generated_job_id?: string | null;
129
+ job_description?: string | null;
130
+ job_address?: string | null;
131
+ status?: string | null;
132
+ date?: string | null;
133
+ edit_date?: string | null;
134
+ company_uuid?: string | null;
135
+ company_name?: string | null;
136
+ purchase_order_number?: string | null;
137
+ active?: number | boolean | null;
138
+ billing_address?: string | null;
139
+ work_done_description?: string | null;
140
+ }
141
+ interface AXEServiceM8PullJobsRequest {
142
+ /** Maximum ServiceM8 jobs to pull into the cache (default 250, max 1000). */
143
+ maxJobs?: number;
144
+ /**
145
+ * When true, runs a full snapshot (no incremental filter) and reconciles deactivations.
146
+ * When false (default for schedules), pulls jobs modified since the last successful sync.
147
+ */
148
+ force?: boolean;
149
+ }
150
+ interface AXEServiceM8PullJobsResponse {
151
+ upserted: number;
152
+ deactivated: number;
153
+ /** True when incremental pull found no modified jobs in ServiceM8. */
154
+ skipped?: boolean;
155
+ /** Whether this run used incremental or full snapshot mode. */
156
+ syncMode?: 'incremental' | 'full';
157
+ }
158
+ interface AXEServiceM8ImportJobRequest {
159
+ remoteRecordId: string;
160
+ projectTypeId: string;
161
+ syncStrategy: AXEPMProjectSyncStrategy;
162
+ }
163
+ interface AXEServiceM8ImportJobResponse {
164
+ platformProjectId: string;
165
+ created: boolean;
166
+ }
167
+ interface AXEServiceM8SyncPlatformProjectRequest {
168
+ platformProjectId: string;
169
+ }
170
+
171
+ /** Public OAuth authorize endpoint (fixed host). */
172
+ declare const AXE_SERVICEM8_OAUTH_AUTHORIZE_URL = "https://go.servicem8.com/oauth/authorize";
173
+ /** Public OAuth token endpoint (authorization_code + refresh_token). */
174
+ declare const AXE_SERVICEM8_OAUTH_TOKEN_URL = "https://go.servicem8.com/oauth/access_token";
175
+ /** REST API base URL stored on connection `instanceUrl`. */
176
+ declare const AXE_SERVICEM8_API_BASE_URL = "https://api.servicem8.com";
177
+ /**
178
+ * OAuth scopes for connect.
179
+ * `manage_jobs` is included so push sync can be added later without re-consent.
180
+ */
181
+ declare const AXE_SERVICEM8_OAUTH_SCOPES = "read_jobs manage_jobs vendor";
182
+
183
+ export { AXEServiceM8CommandKeys, AXEServiceM8MenuKeys, AXEServiceM8Permissions, AXE_SERVICEM8_API_BASE_URL, AXE_SERVICEM8_OAUTH_AUTHORIZE_URL, AXE_SERVICEM8_OAUTH_SCOPES, AXE_SERVICEM8_OAUTH_TOKEN_URL, AXPAdministrationPermissionKeys, RootConfig };
184
+ export type { AXEServiceM8ConnectionConfig, AXEServiceM8ImportJobRequest, AXEServiceM8ImportJobResponse, AXEServiceM8JobsSyncState, AXEServiceM8OAuthCompleteRequest, AXEServiceM8OAuthCompleteResponse, AXEServiceM8OAuthStartRequest, AXEServiceM8OAuthStartResponse, AXEServiceM8OAuthValidateRequest, AXEServiceM8OAuthValidateResponse, AXEServiceM8PullJobsRequest, AXEServiceM8PullJobsResponse, AXEServiceM8RemoteJob, AXEServiceM8SyncPlatformProjectRequest };
@@ -0,0 +1,23 @@
1
+ import * as i0 from '@angular/core';
2
+ import * as i1 from '@acorex-platform/module-integration-management-client';
3
+ import { AXPLogoProvider } from '@acorex-platform/framework-client/core';
4
+
5
+ declare class AXEServiceM8ExtensionModule {
6
+ static ɵfac: i0.ɵɵFactoryDeclaration<AXEServiceM8ExtensionModule, never>;
7
+ static ɵmod: i0.ɵɵNgModuleDeclaration<AXEServiceM8ExtensionModule, never, [typeof i1.AXMIntegrationManagementModule], never>;
8
+ static ɵinj: i0.ɵɵInjectorDeclaration<AXEServiceM8ExtensionModule>;
9
+ }
10
+
11
+ declare const axpServiceM8LogoKey: "servicem8";
12
+ /** Mark artwork (1024×1024); consuming apps copy to `assets/images/brand/integrations/`. */
13
+ declare const AXE_SERVICEM8_MARK_SRC = "assets/images/brand/integrations/servicem8-mark.png";
14
+ /** Source mark is square. */
15
+ declare const AXE_SERVICEM8_MARK_ASPECT = 1;
16
+
17
+ declare const AXE_SERVICEM8_LOGO_PROVIDER: {
18
+ readonly provide: i0.InjectionToken<AXPLogoProvider[]>;
19
+ readonly useValue: AXPLogoProvider;
20
+ readonly multi: true;
21
+ };
22
+
23
+ export { AXEServiceM8ExtensionModule, AXE_SERVICEM8_LOGO_PROVIDER, AXE_SERVICEM8_MARK_ASPECT, AXE_SERVICEM8_MARK_SRC, axpServiceM8LogoKey };