@leapdev/app-platform 0.1.0-beta.3 → 0.1.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +25 -0
- package/package.json +15 -15
- package/project.json +27 -0
- package/src/index.ts +44 -0
- package/src/lib/app-platform.ts +270 -0
- package/src/lib/environment/environment.dictionary.ts +28 -0
- package/src/lib/environment/environment.ts +44 -0
- package/src/lib/environment/{index.d.ts → index.ts} +2 -2
- package/src/lib/environment/leap/env.dev.au.config.ts +33 -0
- package/src/lib/environment/leap/env.live.au.config.ts +32 -0
- package/src/lib/environment/leap/env.live.ca.config.ts +32 -0
- package/src/lib/environment/leap/env.live.nz.config.ts +32 -0
- package/src/lib/environment/leap/env.live.uk.config.ts +32 -0
- package/src/lib/environment/leap/env.live.us.config.ts +32 -0
- package/src/lib/environment/leap/env.liveb.au.config.ts +32 -0
- package/src/lib/environment/leap/env.test.au.config.ts +33 -0
- package/src/lib/environment/leap/env.test.ca.config.ts +33 -0
- package/src/lib/environment/leap/env.test.nz.config.ts +33 -0
- package/src/lib/environment/leap/env.test.uk.config.ts +33 -0
- package/src/lib/environment/leap/env.test.us.config.ts +33 -0
- package/src/lib/functions/helpers.ts +100 -0
- package/src/lib/functions/xml.ts +41 -0
- package/src/lib/models/calc-api.model.ts +74 -0
- package/src/lib/models/constants/index.ts +29 -0
- package/src/lib/models/{index.d.ts → index.ts} +1 -1
- package/src/lib/models/leap-events.model.ts +79 -0
- package/src/lib/models/leap.model.ts +78 -0
- package/src/lib/models/signalr.model.ts +22 -0
- package/src/lib/services/auth/auth.service.ts +219 -0
- package/src/lib/services/calc-api/calc-api-service.interface.ts +12 -0
- package/src/lib/services/calc-api/calc-word.service.ts +117 -0
- package/src/lib/services/calc-api/promise-helper.function.ts +26 -0
- package/src/lib/services/custom-xml-part.service.ts +340 -0
- package/src/lib/services/document/document.service.ts +33 -0
- package/src/lib/services/{index.d.ts → index.ts} +10 -5
- package/src/lib/services/leap-events.service.ts +489 -0
- package/src/lib/services/logger/logger.service.ts +42 -0
- package/src/lib/services/signalr/signalr.service.ts +88 -0
- package/tsconfig.json +20 -0
- package/tsconfig.lib.json +12 -0
- package/src/index.d.ts +0 -25
- package/src/index.js +0 -42
- package/src/index.js.map +0 -1
- package/src/lib/app-platform.d.ts +0 -37
- package/src/lib/app-platform.js +0 -231
- package/src/lib/app-platform.js.map +0 -1
- package/src/lib/environment/environment.d.ts +0 -37
- package/src/lib/environment/environment.dictionary.d.ts +0 -4
- package/src/lib/environment/environment.dictionary.js +0 -30
- package/src/lib/environment/environment.dictionary.js.map +0 -1
- package/src/lib/environment/environment.js +0 -17
- package/src/lib/environment/environment.js.map +0 -1
- package/src/lib/environment/index.js +0 -9
- package/src/lib/environment/index.js.map +0 -1
- package/src/lib/environment/leap/env.dev.au.config.d.ts +0 -32
- package/src/lib/environment/leap/env.dev.au.config.js +0 -36
- package/src/lib/environment/leap/env.dev.au.config.js.map +0 -1
- package/src/lib/environment/leap/env.live.au.config.d.ts +0 -32
- package/src/lib/environment/leap/env.live.au.config.js +0 -36
- package/src/lib/environment/leap/env.live.au.config.js.map +0 -1
- package/src/lib/environment/leap/env.live.ca.config.d.ts +0 -32
- package/src/lib/environment/leap/env.live.ca.config.js +0 -36
- package/src/lib/environment/leap/env.live.ca.config.js.map +0 -1
- package/src/lib/environment/leap/env.live.nz.config.d.ts +0 -32
- package/src/lib/environment/leap/env.live.nz.config.js +0 -36
- package/src/lib/environment/leap/env.live.nz.config.js.map +0 -1
- package/src/lib/environment/leap/env.live.uk.config.d.ts +0 -32
- package/src/lib/environment/leap/env.live.uk.config.js +0 -36
- package/src/lib/environment/leap/env.live.uk.config.js.map +0 -1
- package/src/lib/environment/leap/env.live.us.config.d.ts +0 -32
- package/src/lib/environment/leap/env.live.us.config.js +0 -36
- package/src/lib/environment/leap/env.live.us.config.js.map +0 -1
- package/src/lib/environment/leap/env.liveb.au.config.d.ts +0 -32
- package/src/lib/environment/leap/env.liveb.au.config.js +0 -36
- package/src/lib/environment/leap/env.liveb.au.config.js.map +0 -1
- package/src/lib/environment/leap/env.test.au.config.d.ts +0 -32
- package/src/lib/environment/leap/env.test.au.config.js +0 -36
- package/src/lib/environment/leap/env.test.au.config.js.map +0 -1
- package/src/lib/environment/leap/env.test.ca.config.d.ts +0 -32
- package/src/lib/environment/leap/env.test.ca.config.js +0 -36
- package/src/lib/environment/leap/env.test.ca.config.js.map +0 -1
- package/src/lib/environment/leap/env.test.nz.config.d.ts +0 -32
- package/src/lib/environment/leap/env.test.nz.config.js +0 -36
- package/src/lib/environment/leap/env.test.nz.config.js.map +0 -1
- package/src/lib/environment/leap/env.test.uk.config.d.ts +0 -32
- package/src/lib/environment/leap/env.test.uk.config.js +0 -36
- package/src/lib/environment/leap/env.test.uk.config.js.map +0 -1
- package/src/lib/environment/leap/env.test.us.config.d.ts +0 -32
- package/src/lib/environment/leap/env.test.us.config.js +0 -36
- package/src/lib/environment/leap/env.test.us.config.js.map +0 -1
- package/src/lib/functions/helpers.d.ts +0 -5
- package/src/lib/functions/helpers.js +0 -95
- package/src/lib/functions/helpers.js.map +0 -1
- package/src/lib/functions/xml.d.ts +0 -2
- package/src/lib/functions/xml.js +0 -41
- package/src/lib/functions/xml.js.map +0 -1
- package/src/lib/models/calc-api.model.d.ts +0 -57
- package/src/lib/models/calc-api.model.js +0 -39
- package/src/lib/models/calc-api.model.js.map +0 -1
- package/src/lib/models/constants/index.d.ts +0 -28
- package/src/lib/models/constants/index.js +0 -32
- package/src/lib/models/constants/index.js.map +0 -1
- package/src/lib/models/index.js +0 -5
- package/src/lib/models/index.js.map +0 -1
- package/src/lib/models/leap-events.model.d.ts +0 -56
- package/src/lib/models/leap-events.model.js +0 -71
- package/src/lib/models/leap-events.model.js.map +0 -1
- package/src/lib/models/leap.model.d.ts +0 -63
- package/src/lib/models/leap.model.js +0 -29
- package/src/lib/models/leap.model.js.map +0 -1
- package/src/lib/models/signalr.model.d.ts +0 -21
- package/src/lib/models/signalr.model.js +0 -3
- package/src/lib/models/signalr.model.js.map +0 -1
- package/src/lib/services/auth/auth.service.d.ts +0 -13
- package/src/lib/services/auth/auth.service.js +0 -185
- package/src/lib/services/auth/auth.service.js.map +0 -1
- package/src/lib/services/calc-api/calc-api-service.interface.d.ts +0 -6
- package/src/lib/services/calc-api/calc-api-service.interface.js +0 -3
- package/src/lib/services/calc-api/calc-api-service.interface.js.map +0 -1
- package/src/lib/services/calc-api/calc-word.service.d.ts +0 -12
- package/src/lib/services/calc-api/calc-word.service.js +0 -99
- package/src/lib/services/calc-api/calc-word.service.js.map +0 -1
- package/src/lib/services/calc-api/promise-helper.function.d.ts +0 -2
- package/src/lib/services/calc-api/promise-helper.function.js +0 -32
- package/src/lib/services/calc-api/promise-helper.function.js.map +0 -1
- package/src/lib/services/custom-xml-part.service.d.ts +0 -5
- package/src/lib/services/custom-xml-part.service.js +0 -269
- package/src/lib/services/custom-xml-part.service.js.map +0 -1
- package/src/lib/services/document/document.service.d.ts +0 -16
- package/src/lib/services/document/document.service.js +0 -32
- package/src/lib/services/document/document.service.js.map +0 -1
- package/src/lib/services/index.js +0 -9
- package/src/lib/services/index.js.map +0 -1
- package/src/lib/services/leap-events.service.d.ts +0 -26
- package/src/lib/services/leap-events.service.js +0 -357
- package/src/lib/services/leap-events.service.js.map +0 -1
- package/src/lib/services/logger/logger.service.d.ts +0 -10
- package/src/lib/services/logger/logger.service.js +0 -39
- package/src/lib/services/logger/logger.service.js.map +0 -1
- package/src/lib/services/signalr/signalr.service.d.ts +0 -9
- package/src/lib/services/signalr/signalr.service.js +0 -93
- package/src/lib/services/signalr/signalr.service.js.map +0 -1
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { constants } from '../../models/constants';
|
|
2
|
+
import { LEAPUser } from '../../models/leap.model';
|
|
3
|
+
|
|
4
|
+
export class AuthService {
|
|
5
|
+
readonly #code: string;
|
|
6
|
+
readonly #codeVerifier: string;
|
|
7
|
+
readonly #clientId: string;
|
|
8
|
+
readonly #env: string;
|
|
9
|
+
readonly #authHost: string;
|
|
10
|
+
|
|
11
|
+
#user: LEAPUser | undefined;
|
|
12
|
+
#accessToken: string | undefined;
|
|
13
|
+
#refreshToken: string | undefined;
|
|
14
|
+
|
|
15
|
+
constructor(data: {
|
|
16
|
+
code: string;
|
|
17
|
+
codeVerifier;
|
|
18
|
+
clientId: string;
|
|
19
|
+
env: string;
|
|
20
|
+
}) {
|
|
21
|
+
this.#code = data.code;
|
|
22
|
+
this.#codeVerifier = data.codeVerifier;
|
|
23
|
+
this.#clientId = data.clientId;
|
|
24
|
+
this.#env = data.env.toLowerCase();
|
|
25
|
+
this.#authHost =
|
|
26
|
+
this.#env === 'live'
|
|
27
|
+
? 'https://auth.leap.services'
|
|
28
|
+
: 'https://auth-test.leap.services';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async init(): Promise<boolean> {
|
|
32
|
+
try {
|
|
33
|
+
const data = await this.#exchangeAuthCodeForAccessToken();
|
|
34
|
+
this.#accessToken = data.access_token;
|
|
35
|
+
this.#refreshToken = data.refresh_token;
|
|
36
|
+
this.#user = await this.#getUserInfo();
|
|
37
|
+
return true;
|
|
38
|
+
} catch (e) {
|
|
39
|
+
console.log('[AuthService] unable to init');
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async getRefreshedAccessToken(force?: boolean) {
|
|
45
|
+
if (this.#accessToken === undefined || !this.#accessToken) {
|
|
46
|
+
return '';
|
|
47
|
+
} else {
|
|
48
|
+
const decoded = this.#decodeAccessToken(this.#accessToken);
|
|
49
|
+
|
|
50
|
+
if (
|
|
51
|
+
force ||
|
|
52
|
+
(!!decoded &&
|
|
53
|
+
(decoded.exp - constants.tokenSecondsBeforeExpire) * 1000 <
|
|
54
|
+
Date.now())
|
|
55
|
+
) {
|
|
56
|
+
const data = await this.#exchangeRefreshTokenForAccessToken();
|
|
57
|
+
|
|
58
|
+
if (data) {
|
|
59
|
+
this.#accessToken = data.access_token;
|
|
60
|
+
this.#refreshToken = data.refresh_token;
|
|
61
|
+
}
|
|
62
|
+
return this.#accessToken;
|
|
63
|
+
} else {
|
|
64
|
+
return this.#accessToken;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getLEAPUser() {
|
|
70
|
+
return this.#user;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async #exchangeAuthCodeForAccessToken(): Promise<{
|
|
74
|
+
access_token: string;
|
|
75
|
+
refresh_token: string;
|
|
76
|
+
}> {
|
|
77
|
+
return new Promise<{ access_token: string; refresh_token: string }>(
|
|
78
|
+
(resolve, reject) => {
|
|
79
|
+
this.#xhr<{ access_token: string; refresh_token: string }>(
|
|
80
|
+
{
|
|
81
|
+
method: 'POST',
|
|
82
|
+
endpoint: `${this.#authHost}/oauth/token`,
|
|
83
|
+
options: {
|
|
84
|
+
body: {
|
|
85
|
+
grant_type: 'authorization_code',
|
|
86
|
+
code: this.#code,
|
|
87
|
+
code_verifier: this.#codeVerifier,
|
|
88
|
+
client_id: this.#clientId,
|
|
89
|
+
redirect_uri: constants.fourdRedirectUrl,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
accessToken: undefined,
|
|
93
|
+
},
|
|
94
|
+
(response, status) => {
|
|
95
|
+
if (status === 200) {
|
|
96
|
+
return resolve(response);
|
|
97
|
+
} else return reject(response);
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async #getUserInfo(): Promise<LEAPUser> {
|
|
105
|
+
return new Promise<LEAPUser>((resolve, reject) => {
|
|
106
|
+
this.#xhr<LEAPUser>(
|
|
107
|
+
{
|
|
108
|
+
method: 'GET',
|
|
109
|
+
endpoint: `${this.#authHost}/api/userinfo`,
|
|
110
|
+
options: {},
|
|
111
|
+
accessToken: this.#accessToken,
|
|
112
|
+
},
|
|
113
|
+
(response, status) => {
|
|
114
|
+
if (status === 200) {
|
|
115
|
+
return resolve(response);
|
|
116
|
+
} else return reject(response);
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async #exchangeRefreshTokenForAccessToken(): Promise<{
|
|
123
|
+
access_token: string;
|
|
124
|
+
refresh_token: string;
|
|
125
|
+
} | null> {
|
|
126
|
+
return new Promise<{ access_token: string; refresh_token: string } | null>(
|
|
127
|
+
(resolve, reject) => {
|
|
128
|
+
if (!this.#refreshToken) {
|
|
129
|
+
resolve(null);
|
|
130
|
+
} else {
|
|
131
|
+
this.#xhr<{ access_token: string; refresh_token: string }>(
|
|
132
|
+
{
|
|
133
|
+
method: 'POST',
|
|
134
|
+
endpoint: `${this.#authHost}/oauth/token`,
|
|
135
|
+
options: {
|
|
136
|
+
body: {
|
|
137
|
+
grant_type: 'refresh_token',
|
|
138
|
+
refresh_token: this.#refreshToken,
|
|
139
|
+
code_verifier: this.#codeVerifier,
|
|
140
|
+
client_id: this.#clientId,
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
accessToken: undefined,
|
|
144
|
+
},
|
|
145
|
+
(response, status) => {
|
|
146
|
+
if (status === 200) {
|
|
147
|
+
return resolve(response);
|
|
148
|
+
} else return reject(response);
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
#xhr<T>(
|
|
157
|
+
params: {
|
|
158
|
+
method: string;
|
|
159
|
+
endpoint: string;
|
|
160
|
+
options: any;
|
|
161
|
+
accessToken: string | undefined;
|
|
162
|
+
},
|
|
163
|
+
onload: (resp: T, status: number) => any
|
|
164
|
+
): void {
|
|
165
|
+
const { method, endpoint, accessToken } = params;
|
|
166
|
+
let { options } = params;
|
|
167
|
+
options = {
|
|
168
|
+
...{
|
|
169
|
+
contentType: 'application/json',
|
|
170
|
+
accessControlAllowOrigin: '*',
|
|
171
|
+
body: undefined,
|
|
172
|
+
},
|
|
173
|
+
...options,
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const xhr = new XMLHttpRequest();
|
|
177
|
+
xhr.open(method, endpoint, true);
|
|
178
|
+
xhr.setRequestHeader('Content-type', options.contentType);
|
|
179
|
+
xhr.setRequestHeader(
|
|
180
|
+
'Access-Control-Allow-Origin',
|
|
181
|
+
options.accessControlAllowOrigin
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
if (accessToken)
|
|
185
|
+
xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
|
|
186
|
+
|
|
187
|
+
xhr.onload = () => {
|
|
188
|
+
try {
|
|
189
|
+
let resp;
|
|
190
|
+
if (xhr.response) resp = JSON.parse(xhr.response);
|
|
191
|
+
onload(resp, xhr.status);
|
|
192
|
+
} catch (e) {
|
|
193
|
+
onload(xhr.response, xhr.status);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
if (options.body) options.body = JSON.stringify(options.body);
|
|
198
|
+
|
|
199
|
+
xhr.send(options.body);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
#decodeAccessToken(accessToken: string | undefined) {
|
|
203
|
+
if (!accessToken) {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const payload = accessToken.split('.')[1];
|
|
208
|
+
if (payload) {
|
|
209
|
+
try {
|
|
210
|
+
const result = window.atob(payload);
|
|
211
|
+
return JSON.parse(result);
|
|
212
|
+
} catch (e) {
|
|
213
|
+
throw Error('Fail to decode access token.');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CreateSessionRequest,
|
|
3
|
+
EvaluatePathsRequest,
|
|
4
|
+
} from '../../models/calc-api.model';
|
|
5
|
+
|
|
6
|
+
export interface ICalcApiService {
|
|
7
|
+
createSession(request: CreateSessionRequest): Promise<string>;
|
|
8
|
+
|
|
9
|
+
destroySession(sessionId: string): Promise<void>;
|
|
10
|
+
|
|
11
|
+
evaluatePaths(request: EvaluatePathsRequest): Promise<Array<any> | null>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { v4 as uuid } from 'uuid';
|
|
2
|
+
|
|
3
|
+
import { ICalcApiService } from './calc-api-service.interface';
|
|
4
|
+
import { CreateSessionRequest, EvaluatePathsRequest, EvaluatePathsResponse } from '../../models/calc-api.model';
|
|
5
|
+
import { LeapEventsService } from '../leap-events.service';
|
|
6
|
+
import { LoggerService } from '../logger/logger.service';
|
|
7
|
+
import { LeapEvent, LeapEventAction, LeapEventType } from '../../models/leap-events.model';
|
|
8
|
+
import { timeout } from './promise-helper.function';
|
|
9
|
+
|
|
10
|
+
export class CalcWordService implements ICalcApiService {
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
private leapEventsService: LeapEventsService,
|
|
14
|
+
private loggerService: LoggerService
|
|
15
|
+
) { }
|
|
16
|
+
|
|
17
|
+
createSession(request: CreateSessionRequest): Promise<string> {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
Promise.resolve(() => {
|
|
20
|
+
this.loggerService.log('[CalcApiOfflineService] (createSession) Creating CalcApi session');
|
|
21
|
+
})
|
|
22
|
+
.then(() => {
|
|
23
|
+
// TODO: Get Calc API sessionId from LEAP Desktop and return that
|
|
24
|
+
})
|
|
25
|
+
.then(() => {
|
|
26
|
+
this.loggerService.log('[CalcApiOfflineService] (createSession) LeapEvent CreateSession request sent');
|
|
27
|
+
resolve('');
|
|
28
|
+
})
|
|
29
|
+
.catch((error: any) => {
|
|
30
|
+
this.loggerService.error(`[CalcApiOfflineService] createSession() failed: ${error}`);
|
|
31
|
+
reject(error);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
destroySession(sessionId: string): Promise<void> {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
Promise.resolve(() => {
|
|
39
|
+
this.loggerService.log('[CalcApiOfflineService] (destroySession) Destroying CalcApi session');
|
|
40
|
+
})
|
|
41
|
+
.then(() => {
|
|
42
|
+
// TODO: Should we just fall through here?
|
|
43
|
+
})
|
|
44
|
+
.then(() => {
|
|
45
|
+
this.loggerService.log('[DocumentBuilderService] (destroySession) LeapEvent DestroySession request sent');
|
|
46
|
+
resolve();
|
|
47
|
+
})
|
|
48
|
+
.catch((error: any) => {
|
|
49
|
+
this.loggerService.error(`[DocumentBuilderService] destroySession() failed: ${error}`);
|
|
50
|
+
reject(error);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
evaluatePaths(request: EvaluatePathsRequest): Promise<Array<EvaluatePathsResponse> | null> {
|
|
56
|
+
let subscriptionId: string;
|
|
57
|
+
|
|
58
|
+
// Check for empty matterFields - don't bother hitting the Local DocBuilder API in this case
|
|
59
|
+
if (request == null || (request.paths && request.paths.length === 0)) {
|
|
60
|
+
return Promise.resolve(null);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
Promise.resolve(() => {
|
|
65
|
+
this.loggerService.log(
|
|
66
|
+
'[CalcApiWordService] (evaluatePaths) Evaluating paths on Local Calc API'
|
|
67
|
+
);
|
|
68
|
+
})
|
|
69
|
+
.then(() => {
|
|
70
|
+
const id = new Date().getTime().toString();
|
|
71
|
+
return this.leapEventsService.addEvent({
|
|
72
|
+
id,
|
|
73
|
+
correlationId: null,
|
|
74
|
+
eventType: LeapEventType.CalcApiRequest,
|
|
75
|
+
action: LeapEventAction.Calc_EvaluatePaths,
|
|
76
|
+
data: request
|
|
77
|
+
});
|
|
78
|
+
})
|
|
79
|
+
.then(event => {
|
|
80
|
+
this.loggerService.log('[CalcApiWordService] LeapEvent EvaluatePaths request sent');
|
|
81
|
+
|
|
82
|
+
// Subscribe
|
|
83
|
+
const leapEventResponseHandlerPromise = new Promise<Array<EvaluatePathsResponse>>((innerResolve, innerReject) => {
|
|
84
|
+
subscriptionId = this.leapEventsService.subscribe(
|
|
85
|
+
event.id,
|
|
86
|
+
LeapEventType.CalcApiResponse,
|
|
87
|
+
LeapEventAction.Calc_EvaluatePathsResponse,
|
|
88
|
+
(leapEvent: LeapEvent) => this.leapEventsService.handleLeapEventResponse<Array<EvaluatePathsResponse>>(
|
|
89
|
+
leapEvent, { resolve: innerResolve, reject: innerReject }
|
|
90
|
+
)
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return timeout(leapEventResponseHandlerPromise, 5000);
|
|
95
|
+
})
|
|
96
|
+
.then((result) => {
|
|
97
|
+
// Return result
|
|
98
|
+
resolve(result);
|
|
99
|
+
})
|
|
100
|
+
.catch((error: any) => {
|
|
101
|
+
this.loggerService.error(`[CalcApiWordService] evaluatePaths() failed: ${error}`);
|
|
102
|
+
reject(error);
|
|
103
|
+
})
|
|
104
|
+
.then(() => {
|
|
105
|
+
// Unsubscribe
|
|
106
|
+
return this.leapEventsService.unsubscribe(subscriptionId);
|
|
107
|
+
})
|
|
108
|
+
.then((unsubscribeSuccess: boolean) => {
|
|
109
|
+
if (unsubscribeSuccess === true) {
|
|
110
|
+
this.loggerService.log(`[CalcApiWordService] (evaluatePaths) Successfully unsubscribed ${subscriptionId}`);
|
|
111
|
+
} else {
|
|
112
|
+
this.loggerService.warn(`[CalcApiWordService] (evaluatePaths) Failed to unsubscribe from ${subscriptionId}`);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Return a promise that resolves after duration milliseconds
|
|
3
|
+
*/
|
|
4
|
+
export function sleep(duration: number) {
|
|
5
|
+
return new Promise<void>(function (resolve) {
|
|
6
|
+
setTimeout(() => {
|
|
7
|
+
resolve();
|
|
8
|
+
}, duration);
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
Return a promise that rejects after duration milliseconds
|
|
14
|
+
*/
|
|
15
|
+
export function timeout<T>(promise: Promise<T>, duration: number): Promise<T> {
|
|
16
|
+
if (duration === null || duration <= 0) {
|
|
17
|
+
return promise;
|
|
18
|
+
} else {
|
|
19
|
+
const timeoutPromise = new Promise<T>((resolve, reject) => {
|
|
20
|
+
setTimeout(() => {
|
|
21
|
+
reject(`Timed out after ${duration}ms`);
|
|
22
|
+
}, duration);
|
|
23
|
+
});
|
|
24
|
+
return Promise.race<T>([promise, timeoutPromise]);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/// <reference types="office-js" />
|
|
2
|
+
|
|
3
|
+
import { promisifyAsync } from '../functions/helpers';
|
|
4
|
+
import { toObj, toXml } from '../functions/xml';
|
|
5
|
+
const retryTimes = 5;
|
|
6
|
+
|
|
7
|
+
const getCustomXmlPartsByNamespace = (
|
|
8
|
+
namespace: string
|
|
9
|
+
): Promise<Array<Office.CustomXmlPart> | null> => {
|
|
10
|
+
const officeDocument: Office.Document = Office.context.document;
|
|
11
|
+
if (officeDocument) {
|
|
12
|
+
const officeCustomXmlParts = officeDocument.customXmlParts;
|
|
13
|
+
//todo: use this format instead of promisifyAsync
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
officeCustomXmlParts.getByNamespaceAsync(namespace, (result)=> {
|
|
16
|
+
resolve(result.value)
|
|
17
|
+
})
|
|
18
|
+
} )
|
|
19
|
+
} else {
|
|
20
|
+
return Promise.resolve(null);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
//todo: remove all "any" reference in the project
|
|
25
|
+
const getCustomXmlPartByNamespace = (namespace: string): Promise<any> => {
|
|
26
|
+
return getCustomXmlPartsByNamespace(namespace)
|
|
27
|
+
.then((customXmlParts: any) => {
|
|
28
|
+
if (customXmlParts != null) {
|
|
29
|
+
// We have something
|
|
30
|
+
if (Array.isArray(customXmlParts)) {
|
|
31
|
+
if (customXmlParts.length > 0) {
|
|
32
|
+
return customXmlParts[0]; // Happy path
|
|
33
|
+
} else {
|
|
34
|
+
return null; // No error, but no matching custom XML part either
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
// We have something, but it's not an array
|
|
38
|
+
return customXmlParts;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return null; // No error, but no matching custom XML part either
|
|
42
|
+
})
|
|
43
|
+
.catch((error) => {
|
|
44
|
+
console.error(
|
|
45
|
+
error,
|
|
46
|
+
`[CustomXmlPartService] (getCustomXmlPartByNamespace) Failed to get custom XML part by namespace`
|
|
47
|
+
);
|
|
48
|
+
return null;
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const getCustomXmlPartData = (partOrNamespace: string): Promise<any> => {
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
return new Promise((innerResolve) => {
|
|
55
|
+
if (typeof partOrNamespace === 'string') {
|
|
56
|
+
innerResolve(getCustomXmlPartByNamespace(partOrNamespace));
|
|
57
|
+
} else {
|
|
58
|
+
return innerResolve(partOrNamespace);
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
.then((customXmlPart: any) => {
|
|
62
|
+
if (customXmlPart != null) {
|
|
63
|
+
const getXmlAsync = promisifyAsync(
|
|
64
|
+
customXmlPart.getXmlAsync.bind(customXmlPart)
|
|
65
|
+
);
|
|
66
|
+
return getXmlAsync({});
|
|
67
|
+
} else {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
.then((result) => {
|
|
72
|
+
resolve(toObj(result as string));
|
|
73
|
+
})
|
|
74
|
+
.catch((error) => {
|
|
75
|
+
console.error(
|
|
76
|
+
error,
|
|
77
|
+
`[CustomXmlPartService] (getCustomXmlPartData) Failed to get custom XML part data`
|
|
78
|
+
);
|
|
79
|
+
reject(error);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const getNodes = (
|
|
85
|
+
customXmlPart: Office.CustomXmlPart,
|
|
86
|
+
xPath: string = '/*'
|
|
87
|
+
): Promise<Array<Office.CustomXmlNode>> => {
|
|
88
|
+
const getNodes = promisifyAsync<Array<Office.CustomXmlNode>>(
|
|
89
|
+
customXmlPart.getNodesAsync.bind(customXmlPart)
|
|
90
|
+
);
|
|
91
|
+
return getNodes(xPath);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const setNodeXml = (node: Office.CustomXmlNode, xml: string): Promise<void> => {
|
|
95
|
+
const setXml = promisifyAsync<void>(node.setXmlAsync.bind(node));
|
|
96
|
+
return setXml(xml, {});
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const updateCustomXmlPart = (
|
|
100
|
+
customXmlPart: any,
|
|
101
|
+
xml: string,
|
|
102
|
+
rootTagName: string
|
|
103
|
+
): Promise<void> => {
|
|
104
|
+
rootTagName = rootTagName || '';
|
|
105
|
+
const promises: Array<Promise<void>> = [];
|
|
106
|
+
// If custom XML part exists, replace it
|
|
107
|
+
return getNodes(customXmlPart)
|
|
108
|
+
.then((nodes: Array<Office.CustomXmlNode>) => {
|
|
109
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
110
|
+
const node: any = nodes[i] as Office.CustomXmlNode;
|
|
111
|
+
|
|
112
|
+
if (
|
|
113
|
+
node &&
|
|
114
|
+
node.baseName &&
|
|
115
|
+
node.baseName.toUpperCase() === rootTagName.toUpperCase()
|
|
116
|
+
) {
|
|
117
|
+
// Case-insensitive match
|
|
118
|
+
const promise = setNodeXml(node, xml)
|
|
119
|
+
.then(() => {
|
|
120
|
+
console.log(
|
|
121
|
+
'[CustomXmlPartService] updateCustomXmlPart succeeded'
|
|
122
|
+
);
|
|
123
|
+
})
|
|
124
|
+
.catch((error) => {
|
|
125
|
+
console.error(
|
|
126
|
+
error,
|
|
127
|
+
'[CustomXmlPartService] updateCustomXmlPart failed'
|
|
128
|
+
);
|
|
129
|
+
return Promise.reject(error);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
promises.push(promise);
|
|
133
|
+
} else {
|
|
134
|
+
console.log(`Could not find ${rootTagName} in the custom XML part`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
.then(() => {
|
|
139
|
+
return Promise.all(promises);
|
|
140
|
+
})
|
|
141
|
+
.then(() => {
|
|
142
|
+
return Promise.resolve();
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const addCustomXmlPart = (objOrXml: any, options?: any): Promise<void> => {
|
|
147
|
+
return new Promise<void>((resolve, reject) => {
|
|
148
|
+
Promise.resolve()
|
|
149
|
+
.then(() => {
|
|
150
|
+
// Setup values
|
|
151
|
+
let obj: any; //todo: check this obj
|
|
152
|
+
let xml: string;
|
|
153
|
+
options = options || { rootTagName: '' };
|
|
154
|
+
if (typeof objOrXml === 'string' || objOrXml instanceof String) {
|
|
155
|
+
obj = toObj(objOrXml as string);
|
|
156
|
+
xml = objOrXml as string;
|
|
157
|
+
} else {
|
|
158
|
+
obj = objOrXml;
|
|
159
|
+
xml = toXml(objOrXml, options.rootTagName, options);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const officeDocument: Office.Document = Office.context.document;
|
|
163
|
+
const officeCustomXmlParts = officeDocument.customXmlParts;
|
|
164
|
+
const add = promisifyAsync<Office.CustomXmlPart>(
|
|
165
|
+
officeCustomXmlParts.addAsync.bind(officeCustomXmlParts)
|
|
166
|
+
);
|
|
167
|
+
return add(xml, {});
|
|
168
|
+
})
|
|
169
|
+
.then((customXmlPart) => {
|
|
170
|
+
if (customXmlPart == null) {
|
|
171
|
+
// TODO
|
|
172
|
+
}
|
|
173
|
+
resolve();
|
|
174
|
+
})
|
|
175
|
+
.catch((error) => {
|
|
176
|
+
console.error(
|
|
177
|
+
error,
|
|
178
|
+
`[CustomXmlPartService] (addCustomXmlPart) Failed to add custom XML part`
|
|
179
|
+
);
|
|
180
|
+
reject(error);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const updateCustomXmlPartData = (
|
|
186
|
+
partOrNamespace: Office.CustomXmlPart | string,
|
|
187
|
+
objOrXml: any,
|
|
188
|
+
options?: any
|
|
189
|
+
): Promise<boolean> => {
|
|
190
|
+
return new Promise<boolean>((resolve, reject) => {
|
|
191
|
+
return new Promise<Office.CustomXmlPart | null>((innerResolve) => {
|
|
192
|
+
if (typeof partOrNamespace === 'string' || partOrNamespace instanceof String) {
|
|
193
|
+
innerResolve(getCustomXmlPartByNamespace(partOrNamespace as string));
|
|
194
|
+
} else {
|
|
195
|
+
return innerResolve(partOrNamespace);
|
|
196
|
+
}
|
|
197
|
+
})
|
|
198
|
+
.then((customXmlPart) => {
|
|
199
|
+
// Setup values
|
|
200
|
+
let obj: any;
|
|
201
|
+
let xml: string;
|
|
202
|
+
options = options || { rootTagName: '' };
|
|
203
|
+
if (typeof objOrXml === 'string' || objOrXml instanceof String) {
|
|
204
|
+
obj = toObj(objOrXml as string);
|
|
205
|
+
xml = objOrXml as string;
|
|
206
|
+
} else {
|
|
207
|
+
obj = objOrXml;
|
|
208
|
+
xml = toXml(objOrXml, options.rootTagName, options);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (customXmlPart != null) {
|
|
212
|
+
return updateCustomXmlPart(customXmlPart, xml, options.rootTagName);
|
|
213
|
+
} else {
|
|
214
|
+
return addCustomXmlPart(xml, options);
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
.then(() => {
|
|
218
|
+
resolve(true);
|
|
219
|
+
})
|
|
220
|
+
.catch((error) => {
|
|
221
|
+
console.error(
|
|
222
|
+
error,
|
|
223
|
+
`[CustomXmlPartService] (updateCustomXmlPartData) Failed to update ` +
|
|
224
|
+
`${
|
|
225
|
+
typeof partOrNamespace === 'string' ? partOrNamespace : ''
|
|
226
|
+
} custom XML part`
|
|
227
|
+
);
|
|
228
|
+
reject(error);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const retryUpdateCustomXmlPartData = (
|
|
234
|
+
namespace: string,
|
|
235
|
+
data: string,
|
|
236
|
+
options: { rootTagName: string } & { [key: string]: any },
|
|
237
|
+
retries = retryTimes
|
|
238
|
+
): Promise<void> => {
|
|
239
|
+
const { rootTagName } = options;
|
|
240
|
+
return new Promise<void>((resolve, reject) => {
|
|
241
|
+
console.log(
|
|
242
|
+
`[CustomXmlPartService] Updating ${namespace} tag ${rootTagName}`
|
|
243
|
+
);
|
|
244
|
+
updateCustomXmlPartData(namespace, data, options)
|
|
245
|
+
.then(() => {
|
|
246
|
+
resolve();
|
|
247
|
+
})
|
|
248
|
+
.catch((error) => {
|
|
249
|
+
if (retries > 0) {
|
|
250
|
+
console.log(
|
|
251
|
+
`[CustomXmlPartService] Update ${namespace} tag ${rootTagName} failed - Retrying ${
|
|
252
|
+
retryTimes - retries + 1
|
|
253
|
+
}...`
|
|
254
|
+
);
|
|
255
|
+
setTimeout(() => {
|
|
256
|
+
retryUpdateCustomXmlPartData(namespace, data, options, retries - 1)
|
|
257
|
+
.then(resolve)
|
|
258
|
+
.catch(reject);
|
|
259
|
+
}, 2000);
|
|
260
|
+
} else {
|
|
261
|
+
reject(error);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
export const getDataByNamespace = (
|
|
268
|
+
namespace: string,
|
|
269
|
+
retries = 6
|
|
270
|
+
): Promise<any> => {
|
|
271
|
+
return new Promise((resolve, reject) => {
|
|
272
|
+
getCustomXmlPartData(namespace)
|
|
273
|
+
.then((data) => {
|
|
274
|
+
resolve(data);
|
|
275
|
+
})
|
|
276
|
+
.catch((_) => {
|
|
277
|
+
if (retries > 0) {
|
|
278
|
+
console.log(
|
|
279
|
+
`[CustomXmlPartService] Get data from ${namespace} failed - Retrying ${
|
|
280
|
+
retryTimes - retries + 1
|
|
281
|
+
}...`
|
|
282
|
+
);
|
|
283
|
+
setTimeout(() => {
|
|
284
|
+
getDataByNamespace(namespace, retries - 1)
|
|
285
|
+
.then(resolve)
|
|
286
|
+
.catch(reject);
|
|
287
|
+
}, 2000);
|
|
288
|
+
} else {
|
|
289
|
+
reject(`Unable to get custom XML part using namespace ${namespace}`);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
export const updateXmlDataByNamespace = (
|
|
296
|
+
namespace: string,
|
|
297
|
+
data: string,
|
|
298
|
+
rootTagName: string
|
|
299
|
+
): Promise<void> => {
|
|
300
|
+
const options = { rootTagName };
|
|
301
|
+
return retryUpdateCustomXmlPartData(namespace, data, options);
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
export const addHandlerForCustomXmlPart = (
|
|
305
|
+
namespace: string,
|
|
306
|
+
eventType: Office.EventType,
|
|
307
|
+
handler: any
|
|
308
|
+
): Promise<void | null> => {
|
|
309
|
+
return getCustomXmlPartByNamespace(namespace).then((customXmlPart: any) => {
|
|
310
|
+
if (customXmlPart != null) {
|
|
311
|
+
const addHandler = promisifyAsync<void>(
|
|
312
|
+
customXmlPart.addHandlerAsync.bind(customXmlPart)
|
|
313
|
+
);
|
|
314
|
+
return addHandler(eventType, handler, {});
|
|
315
|
+
} else {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
export const getChildNodes = (
|
|
322
|
+
node: Office.CustomXmlNode,
|
|
323
|
+
xPath: string = '*'
|
|
324
|
+
): Promise<Array<Office.CustomXmlNode>> => {
|
|
325
|
+
const getNodes = promisifyAsync<Array<Office.CustomXmlNode>>(
|
|
326
|
+
node.getNodesAsync.bind(node)
|
|
327
|
+
);
|
|
328
|
+
return getNodes(xPath);
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
export const getNodeText = (
|
|
332
|
+
node: Office.CustomXmlNode | null
|
|
333
|
+
): Promise<string> => {
|
|
334
|
+
if (node) {
|
|
335
|
+
const getText = promisifyAsync<string>(node.getTextAsync.bind(node));
|
|
336
|
+
return getText({});
|
|
337
|
+
} else {
|
|
338
|
+
return Promise.resolve('');
|
|
339
|
+
}
|
|
340
|
+
};
|