@kwiz/node 1.0.14 → 1.0.17
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/.github/workflows/npm-publish.yml +24 -0
- package/.madgerc +2 -2
- package/LICENSE +21 -21
- package/README.md +2 -2
- package/dist/axios.d.ts +5 -1
- package/dist/axios.js +14 -1
- package/dist/axios.js.map +1 -1
- package/dist/exports-index.d.ts +1 -0
- package/dist/exports-index.js +1 -0
- package/dist/exports-index.js.map +1 -1
- package/dist/get-with-cache.d.ts +12 -0
- package/dist/get-with-cache.js +43 -0
- package/dist/get-with-cache.js.map +1 -0
- package/fix-folder-imports.js +26 -26
- package/package.json +78 -78
- package/src/SPO/common.ts +16 -16
- package/src/auth/discovery.test.js +8 -8
- package/src/auth/discovery.ts +60 -60
- package/src/auth/msal.ts +43 -43
- package/src/axios.ts +44 -31
- package/src/exports-index.ts +1 -0
- package/src/get-with-cache.ts +38 -0
- package/src/graph/graph.ts +17 -17
- package/src/index.ts +1 -1
- package/src/storage/common.ts +15 -15
- package/src/storage/odata.ts +86 -86
- package/src/storage/table-storage.test.js +134 -134
- package/src/storage/table-storage.ts +313 -313
- package/__azurite_db_blob__.json +0 -1
- package/__azurite_db_blob_extent__.json +0 -1
- package/__azurite_db_queue__.json +0 -1
- package/__azurite_db_queue_extent__.json +0 -1
- package/__azurite_db_table__.json +0 -1
- package/dist/SPO/index.d.ts +0 -1
- package/dist/SPO/index.js +0 -18
- package/dist/SPO/index.js.map +0 -1
- package/dist/auth/index.d.ts +0 -2
- package/dist/auth/index.js +0 -19
- package/dist/auth/index.js.map +0 -1
package/src/auth/msal.ts
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
import { ConfidentialClientApplication } from "@azure/msal-node";
|
|
2
|
-
import { AuthContextType, AuthenticationModes, ITenantInfo } from "@kwiz/common";
|
|
3
|
-
//find tenant id? https://login.microsoftonline.com/kwizcom.onmicrosoft.com/.well-known/openid-configuration
|
|
4
|
-
//https://stackoverflow.com/questions/54771270/msal-ad-token-not-valid-with-sharepoint-online-csom
|
|
5
|
-
|
|
6
|
-
var apps: { [tenant: string]: ConfidentialClientApplication } = {};
|
|
7
|
-
|
|
8
|
-
function GetApp(tenantInfo: ITenantInfo, auth: AuthContextType) {
|
|
9
|
-
let key = `${tenantInfo.idOrName}|${auth.authenticationMode}`
|
|
10
|
-
if (!apps[key]) {
|
|
11
|
-
auth.authenticationMode === AuthenticationModes.clientSecret
|
|
12
|
-
? apps[key] = new ConfidentialClientApplication({
|
|
13
|
-
auth: {
|
|
14
|
-
clientId: auth.clientId,
|
|
15
|
-
authority: tenantInfo.authorityUrl,
|
|
16
|
-
clientSecret: auth.clientSecret
|
|
17
|
-
},
|
|
18
|
-
|
|
19
|
-
})
|
|
20
|
-
: apps[key] = new ConfidentialClientApplication({
|
|
21
|
-
auth: {
|
|
22
|
-
clientId: auth.clientId,
|
|
23
|
-
authority: tenantInfo.authorityUrl,
|
|
24
|
-
clientCertificate: {
|
|
25
|
-
thumbprint: auth.thumbprint,
|
|
26
|
-
privateKey: auth.privateKey
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
return apps[key];
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** client secret not supported by SharePoint, must use certificate */
|
|
36
|
-
export async function GetMSALToken(tenantInfo: ITenantInfo, scope: string, auth: AuthContextType, clearCache?: boolean) {
|
|
37
|
-
const app = GetApp(tenantInfo, auth);
|
|
38
|
-
if (clearCache)
|
|
39
|
-
app.clearCache();
|
|
40
|
-
let token = await app.acquireTokenByClientCredential({
|
|
41
|
-
scopes: [`${scope}/.default`]
|
|
42
|
-
});
|
|
43
|
-
return token.accessToken;
|
|
1
|
+
import { ConfidentialClientApplication } from "@azure/msal-node";
|
|
2
|
+
import { AuthContextType, AuthenticationModes, ITenantInfo } from "@kwiz/common";
|
|
3
|
+
//find tenant id? https://login.microsoftonline.com/kwizcom.onmicrosoft.com/.well-known/openid-configuration
|
|
4
|
+
//https://stackoverflow.com/questions/54771270/msal-ad-token-not-valid-with-sharepoint-online-csom
|
|
5
|
+
|
|
6
|
+
var apps: { [tenant: string]: ConfidentialClientApplication } = {};
|
|
7
|
+
|
|
8
|
+
function GetApp(tenantInfo: ITenantInfo, auth: AuthContextType) {
|
|
9
|
+
let key = `${tenantInfo.idOrName}|${auth.authenticationMode}`
|
|
10
|
+
if (!apps[key]) {
|
|
11
|
+
auth.authenticationMode === AuthenticationModes.clientSecret
|
|
12
|
+
? apps[key] = new ConfidentialClientApplication({
|
|
13
|
+
auth: {
|
|
14
|
+
clientId: auth.clientId,
|
|
15
|
+
authority: tenantInfo.authorityUrl,
|
|
16
|
+
clientSecret: auth.clientSecret
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
})
|
|
20
|
+
: apps[key] = new ConfidentialClientApplication({
|
|
21
|
+
auth: {
|
|
22
|
+
clientId: auth.clientId,
|
|
23
|
+
authority: tenantInfo.authorityUrl,
|
|
24
|
+
clientCertificate: {
|
|
25
|
+
thumbprint: auth.thumbprint,
|
|
26
|
+
privateKey: auth.privateKey
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return apps[key];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** client secret not supported by SharePoint, must use certificate */
|
|
36
|
+
export async function GetMSALToken(tenantInfo: ITenantInfo, scope: string, auth: AuthContextType, clearCache?: boolean) {
|
|
37
|
+
const app = GetApp(tenantInfo, auth);
|
|
38
|
+
if (clearCache)
|
|
39
|
+
app.clearCache();
|
|
40
|
+
let token = await app.acquireTokenByClientCredential({
|
|
41
|
+
scopes: [`${scope}/.default`]
|
|
42
|
+
});
|
|
43
|
+
return token.accessToken;
|
|
44
44
|
}
|
package/src/axios.ts
CHANGED
|
@@ -1,32 +1,45 @@
|
|
|
1
|
-
import { isNullOrEmptyString } from "@kwiz/common";
|
|
2
|
-
import { AxiosRequestConfig } from "axios";
|
|
3
|
-
import { Agent, globalAgent } from "https";
|
|
4
|
-
|
|
5
|
-
type axiosConfigOptions = {
|
|
6
|
-
contantType?: "application/json" | "application/json; odata=nometadata" | "application/xml";
|
|
7
|
-
}
|
|
8
|
-
export function getAxiosConfigBearer(token: string, options?: axiosConfigOptions) {
|
|
9
|
-
return getAxiosConfig(`Bearer ${token}`, options);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function getAxiosConfig(token?: string, options?: axiosConfigOptions) {
|
|
13
|
-
//allow self sign ssl certificates
|
|
14
|
-
globalAgent.options.rejectUnauthorized = false;
|
|
15
|
-
const config: AxiosRequestConfig<any> = {
|
|
16
|
-
httpAgent: new Agent({
|
|
17
|
-
rejectUnauthorized: false
|
|
18
|
-
}),
|
|
19
|
-
headers: {}
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
if (!isNullOrEmptyString(token))
|
|
23
|
-
config.headers!.Authorization = token;
|
|
24
|
-
|
|
25
|
-
if (options) {
|
|
26
|
-
if (!isNullOrEmptyString(options.contantType)) {
|
|
27
|
-
config.headers!["Content-Type"] = options.contantType;
|
|
28
|
-
config.headers!["Accept"] = options.contantType;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return config;
|
|
1
|
+
import { isNullOrEmptyString } from "@kwiz/common";
|
|
2
|
+
import { AxiosError, AxiosRequestConfig } from "axios";
|
|
3
|
+
import { Agent, globalAgent } from "https";
|
|
4
|
+
|
|
5
|
+
type axiosConfigOptions = {
|
|
6
|
+
contantType?: "application/json" | "application/json; odata=nometadata" | "application/xml";
|
|
7
|
+
}
|
|
8
|
+
export function getAxiosConfigBearer(token: string, options?: axiosConfigOptions) {
|
|
9
|
+
return getAxiosConfig(`Bearer ${token}`, options);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getAxiosConfig(token?: string, options?: axiosConfigOptions) {
|
|
13
|
+
//allow self sign ssl certificates
|
|
14
|
+
globalAgent.options.rejectUnauthorized = false;
|
|
15
|
+
const config: AxiosRequestConfig<any> = {
|
|
16
|
+
httpAgent: new Agent({
|
|
17
|
+
rejectUnauthorized: false
|
|
18
|
+
}),
|
|
19
|
+
headers: {}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
if (!isNullOrEmptyString(token))
|
|
23
|
+
config.headers!.Authorization = token;
|
|
24
|
+
|
|
25
|
+
if (options) {
|
|
26
|
+
if (!isNullOrEmptyString(options.contantType)) {
|
|
27
|
+
config.headers!["Content-Type"] = options.contantType;
|
|
28
|
+
config.headers!["Accept"] = options.contantType;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return config;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getAxiosErrorData(error: AxiosError) {
|
|
35
|
+
let code = error.code || "Unknown";
|
|
36
|
+
let errorMessage = error.message || "Unspecified error";
|
|
37
|
+
if (error && error.response && error.response.data && error.response.data["odata.error"]) {
|
|
38
|
+
let errorData: { code: string; message: { value: string; }; } = error.response.data["odata.error"];
|
|
39
|
+
if (errorData.message && errorData.message.value)
|
|
40
|
+
errorMessage = errorData.message.value;
|
|
41
|
+
if (errorData && errorData.code)
|
|
42
|
+
code = errorData.code;
|
|
43
|
+
}
|
|
44
|
+
return { code: code, message: errorMessage };
|
|
32
45
|
}
|
package/src/exports-index.ts
CHANGED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { IDictionary, isNullOrUndefined } from "@kwiz/common";
|
|
2
|
+
|
|
3
|
+
const $$cache: IDictionary<{ expires: Date; value: any }> = {};
|
|
4
|
+
|
|
5
|
+
export async function getWithCache<T>(worker: () => Promise<{ success: boolean; value: T }>, info: {
|
|
6
|
+
/** seconds */
|
|
7
|
+
successCacheDuration: number;
|
|
8
|
+
/** seconds */
|
|
9
|
+
failedCacheDuration?: number;
|
|
10
|
+
/** must be unique for your call! function name, and parameters */
|
|
11
|
+
cacheKey: string;
|
|
12
|
+
forceRefresh?: boolean;
|
|
13
|
+
}) {
|
|
14
|
+
const now = new Date();
|
|
15
|
+
//purge old values
|
|
16
|
+
Object.keys($$cache).forEach(key => {
|
|
17
|
+
if ($$cache[key].expires < now) delete $$cache[key];
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
let cached = info.forceRefresh ? null : $$cache[info.cacheKey];
|
|
21
|
+
|
|
22
|
+
if (isNullOrUndefined(cached)) {
|
|
23
|
+
const result = await worker();
|
|
24
|
+
if (result.success) {
|
|
25
|
+
$$cache[info.cacheKey] = {
|
|
26
|
+
expires: new Date(new Date().getTime() + info.successCacheDuration * 1000),
|
|
27
|
+
value: result.value
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
else if (info.failedCacheDuration > 0) {
|
|
31
|
+
$$cache[info.cacheKey] = {
|
|
32
|
+
expires: new Date(new Date().getTime() + info.failedCacheDuration * 1000),
|
|
33
|
+
value: result.value
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return $$cache[info.cacheKey].value;
|
|
38
|
+
}
|
package/src/graph/graph.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { AuthContextType, ITenantInfo, isNullOrUndefined } from "@kwiz/common";
|
|
2
|
-
import { GetMSALToken } from "../auth/msal";
|
|
3
|
-
import { getAxiosConfigBearer } from "../axios";
|
|
4
|
-
|
|
5
|
-
/** "https://graph.microsoft.com" */
|
|
6
|
-
export const graphScope = "https://graph.microsoft.com";
|
|
7
|
-
|
|
8
|
-
var auth: AuthContextType = null;
|
|
9
|
-
export function ConfigureGraphAuth(config?: AuthContextType) {
|
|
10
|
-
auth = config;
|
|
11
|
-
}
|
|
12
|
-
export async function getAxiosConfigGraph(tenantInfo: ITenantInfo, clearCache?: boolean) {
|
|
13
|
-
if (isNullOrUndefined(auth)) throw Error("Call ConfigureGraphAuth first");
|
|
14
|
-
|
|
15
|
-
// secret or certificate supported
|
|
16
|
-
let token = await GetMSALToken(tenantInfo, graphScope, auth, clearCache);
|
|
17
|
-
return getAxiosConfigBearer(token);
|
|
1
|
+
import { AuthContextType, ITenantInfo, isNullOrUndefined } from "@kwiz/common";
|
|
2
|
+
import { GetMSALToken } from "../auth/msal";
|
|
3
|
+
import { getAxiosConfigBearer } from "../axios";
|
|
4
|
+
|
|
5
|
+
/** "https://graph.microsoft.com" */
|
|
6
|
+
export const graphScope = "https://graph.microsoft.com";
|
|
7
|
+
|
|
8
|
+
var auth: AuthContextType = null;
|
|
9
|
+
export function ConfigureGraphAuth(config?: AuthContextType) {
|
|
10
|
+
auth = config;
|
|
11
|
+
}
|
|
12
|
+
export async function getAxiosConfigGraph(tenantInfo: ITenantInfo, clearCache?: boolean) {
|
|
13
|
+
if (isNullOrUndefined(auth)) throw Error("Call ConfigureGraphAuth first");
|
|
14
|
+
|
|
15
|
+
// secret or certificate supported
|
|
16
|
+
let token = await GetMSALToken(tenantInfo, graphScope, auth, clearCache);
|
|
17
|
+
return getAxiosConfigBearer(token);
|
|
18
18
|
}
|
package/src/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "./exports-index";
|
|
1
|
+
export * from "./exports-index";
|
package/src/storage/common.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { isNullOrEmptyString } from "@kwiz/common";
|
|
2
|
-
import axios from "axios";
|
|
3
|
-
|
|
4
|
-
export async function IsAzuriteRunning() {
|
|
5
|
-
let responseServer = "";
|
|
6
|
-
try {
|
|
7
|
-
//make a request for http://127.0.0.1:10000/ expect response headers "Server" to contain "Azurite"
|
|
8
|
-
const result = await axios.get("http://127.0.0.1:10000/");
|
|
9
|
-
responseServer = result.headers.server;
|
|
10
|
-
} catch (e) {
|
|
11
|
-
responseServer = e.response && e.response.headers && e.response.headers.server;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return isNullOrEmptyString(responseServer) ? false : responseServer.toLowerCase().indexOf("azurite") >= 0;
|
|
15
|
-
}
|
|
1
|
+
import { isNullOrEmptyString } from "@kwiz/common";
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
|
|
4
|
+
export async function IsAzuriteRunning() {
|
|
5
|
+
let responseServer = "";
|
|
6
|
+
try {
|
|
7
|
+
//make a request for http://127.0.0.1:10000/ expect response headers "Server" to contain "Azurite"
|
|
8
|
+
const result = await axios.get("http://127.0.0.1:10000/");
|
|
9
|
+
responseServer = result.headers.server;
|
|
10
|
+
} catch (e) {
|
|
11
|
+
responseServer = e.response && e.response.headers && e.response.headers.server;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return isNullOrEmptyString(responseServer) ? false : responseServer.toLowerCase().indexOf("azurite") >= 0;
|
|
15
|
+
}
|
package/src/storage/odata.ts
CHANGED
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
import { isNullOrEmptyArray, isNullOrEmptyString, isNullOrUndefined } from "@kwiz/common";
|
|
2
|
-
|
|
3
|
-
export enum ODataOperators {
|
|
4
|
-
equal = "eq",
|
|
5
|
-
notEqual = "ne",
|
|
6
|
-
greater = "gt",
|
|
7
|
-
greaterOrEqual = "ge",
|
|
8
|
-
less = "lt",
|
|
9
|
-
lessOrEqual = "le",
|
|
10
|
-
contains = "in",
|
|
11
|
-
startswith = "startswith"
|
|
12
|
-
}
|
|
13
|
-
export enum ODataJoinOperators {
|
|
14
|
-
and = "and",
|
|
15
|
-
or = "or"
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface IOdataFilter<DataType> {
|
|
19
|
-
property: keyof DataType;
|
|
20
|
-
operator: ODataOperators;
|
|
21
|
-
value: string | Date | number | boolean;
|
|
22
|
-
not?: boolean;
|
|
23
|
-
}
|
|
24
|
-
export interface IOdataFilterStatement<DataType> {
|
|
25
|
-
filters: (IOdataFilter<DataType> | IOdataFilterStatement<DataType>)[];
|
|
26
|
-
/** default: or */
|
|
27
|
-
join?: ODataJoinOperators;
|
|
28
|
-
not?: boolean;
|
|
29
|
-
}
|
|
30
|
-
function isIOdataFilter<DataType>(f: IOdataFilter<DataType> | IOdataFilterStatement<DataType>): f is IOdataFilter<DataType> {
|
|
31
|
-
if (typeof (f as IOdataFilter<DataType>).property === "string")
|
|
32
|
-
return true;
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
function isIOdataFilterStatement<DataType>(f: IOdataFilter<DataType> | IOdataFilterStatement<DataType>): f is IOdataFilter<DataType> {
|
|
36
|
-
if (typeof (f as IOdataFilterStatement<DataType>).filters === "object" && Array.isArray((f as IOdataFilterStatement<DataType>).filters))
|
|
37
|
-
return true;
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
enum TableStorageKnownColumns {
|
|
42
|
-
partitionKey = "PartitionKey",
|
|
43
|
-
rowKey = "RowKey"
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function getPropNameForFilter(prop: string | number | symbol) {
|
|
47
|
-
return TableStorageKnownColumns[prop] || prop as string;
|
|
48
|
-
}
|
|
49
|
-
function getOdataFilterStatement<DataType>(filter: IOdataFilter<DataType>) {
|
|
50
|
-
let filterValue = isNullOrUndefined(filter.value)
|
|
51
|
-
? `null`
|
|
52
|
-
: typeof filter.value === "string"
|
|
53
|
-
? `'${filter.value.replace(/'/g, "''")}'`
|
|
54
|
-
: filter.value instanceof Date
|
|
55
|
-
? `'${filter.value.toISOString()}'`
|
|
56
|
-
: `${filter.value}`;
|
|
57
|
-
|
|
58
|
-
if (filter.operator === ODataOperators.startswith)
|
|
59
|
-
return `${filter.not ? 'not ' : ''}${filter.operator}(${getPropNameForFilter(filter.property)}, ${filterValue})`;
|
|
60
|
-
return `${filter.not ? 'not ' : ''}${getPropNameForFilter(filter.property)} ${filter.operator} ${filterValue}`;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function getOdataFilter<DataType>(statement: IOdataFilterStatement<DataType>) {
|
|
64
|
-
if (isNullOrUndefined(statement) || isNullOrEmptyArray(statement.filters)) return "";
|
|
65
|
-
if (isNullOrEmptyString(statement.join)) statement.join = ODataJoinOperators.or;
|
|
66
|
-
|
|
67
|
-
let filterStatements: string[] = [];
|
|
68
|
-
statement.filters.forEach(filter => {
|
|
69
|
-
if (isIOdataFilter(filter)) {
|
|
70
|
-
filterStatements.push(getOdataFilterStatement<DataType>(filter));
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
let subStatement = getOdataFilter(filter);
|
|
74
|
-
if (!isNullOrEmptyString(subStatement))
|
|
75
|
-
filterStatements.push(subStatement);
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
if (filterStatements.length === 0) return "";
|
|
79
|
-
|
|
80
|
-
let result = "";
|
|
81
|
-
if (filterStatements.length === 1) result = filterStatements[0];
|
|
82
|
-
else if (filterStatements.length > 1) {
|
|
83
|
-
result = `(${filterStatements.join(` ${statement.join} `)})`;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return `${statement.not ? '(not ' : ''}${result}${statement.not ? ')' : ''}`;
|
|
1
|
+
import { isNullOrEmptyArray, isNullOrEmptyString, isNullOrUndefined } from "@kwiz/common";
|
|
2
|
+
|
|
3
|
+
export enum ODataOperators {
|
|
4
|
+
equal = "eq",
|
|
5
|
+
notEqual = "ne",
|
|
6
|
+
greater = "gt",
|
|
7
|
+
greaterOrEqual = "ge",
|
|
8
|
+
less = "lt",
|
|
9
|
+
lessOrEqual = "le",
|
|
10
|
+
contains = "in",
|
|
11
|
+
startswith = "startswith"
|
|
12
|
+
}
|
|
13
|
+
export enum ODataJoinOperators {
|
|
14
|
+
and = "and",
|
|
15
|
+
or = "or"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface IOdataFilter<DataType> {
|
|
19
|
+
property: keyof DataType;
|
|
20
|
+
operator: ODataOperators;
|
|
21
|
+
value: string | Date | number | boolean;
|
|
22
|
+
not?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface IOdataFilterStatement<DataType> {
|
|
25
|
+
filters: (IOdataFilter<DataType> | IOdataFilterStatement<DataType>)[];
|
|
26
|
+
/** default: or */
|
|
27
|
+
join?: ODataJoinOperators;
|
|
28
|
+
not?: boolean;
|
|
29
|
+
}
|
|
30
|
+
function isIOdataFilter<DataType>(f: IOdataFilter<DataType> | IOdataFilterStatement<DataType>): f is IOdataFilter<DataType> {
|
|
31
|
+
if (typeof (f as IOdataFilter<DataType>).property === "string")
|
|
32
|
+
return true;
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
function isIOdataFilterStatement<DataType>(f: IOdataFilter<DataType> | IOdataFilterStatement<DataType>): f is IOdataFilter<DataType> {
|
|
36
|
+
if (typeof (f as IOdataFilterStatement<DataType>).filters === "object" && Array.isArray((f as IOdataFilterStatement<DataType>).filters))
|
|
37
|
+
return true;
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
enum TableStorageKnownColumns {
|
|
42
|
+
partitionKey = "PartitionKey",
|
|
43
|
+
rowKey = "RowKey"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getPropNameForFilter(prop: string | number | symbol) {
|
|
47
|
+
return TableStorageKnownColumns[prop] || prop as string;
|
|
48
|
+
}
|
|
49
|
+
function getOdataFilterStatement<DataType>(filter: IOdataFilter<DataType>) {
|
|
50
|
+
let filterValue = isNullOrUndefined(filter.value)
|
|
51
|
+
? `null`
|
|
52
|
+
: typeof filter.value === "string"
|
|
53
|
+
? `'${filter.value.replace(/'/g, "''")}'`
|
|
54
|
+
: filter.value instanceof Date
|
|
55
|
+
? `'${filter.value.toISOString()}'`
|
|
56
|
+
: `${filter.value}`;
|
|
57
|
+
|
|
58
|
+
if (filter.operator === ODataOperators.startswith)
|
|
59
|
+
return `${filter.not ? 'not ' : ''}${filter.operator}(${getPropNameForFilter(filter.property)}, ${filterValue})`;
|
|
60
|
+
return `${filter.not ? 'not ' : ''}${getPropNameForFilter(filter.property)} ${filter.operator} ${filterValue}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function getOdataFilter<DataType>(statement: IOdataFilterStatement<DataType>) {
|
|
64
|
+
if (isNullOrUndefined(statement) || isNullOrEmptyArray(statement.filters)) return "";
|
|
65
|
+
if (isNullOrEmptyString(statement.join)) statement.join = ODataJoinOperators.or;
|
|
66
|
+
|
|
67
|
+
let filterStatements: string[] = [];
|
|
68
|
+
statement.filters.forEach(filter => {
|
|
69
|
+
if (isIOdataFilter(filter)) {
|
|
70
|
+
filterStatements.push(getOdataFilterStatement<DataType>(filter));
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
let subStatement = getOdataFilter(filter);
|
|
74
|
+
if (!isNullOrEmptyString(subStatement))
|
|
75
|
+
filterStatements.push(subStatement);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
if (filterStatements.length === 0) return "";
|
|
79
|
+
|
|
80
|
+
let result = "";
|
|
81
|
+
if (filterStatements.length === 1) result = filterStatements[0];
|
|
82
|
+
else if (filterStatements.length > 1) {
|
|
83
|
+
result = `(${filterStatements.join(` ${statement.join} `)})`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return `${statement.not ? '(not ' : ''}${result}${statement.not ? ')' : ''}`;
|
|
87
87
|
}
|