@fluidframework/azure-client 0.59.3000 → 1.0.0
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.js +1 -1
- package/README.md +7 -8
- package/dist/AzureClient.d.ts +22 -5
- package/dist/AzureClient.d.ts.map +1 -1
- package/dist/AzureClient.js +87 -20
- package/dist/AzureClient.js.map +1 -1
- package/dist/AzureUrlResolver.d.ts +1 -1
- package/dist/AzureUrlResolver.d.ts.map +1 -1
- package/dist/AzureUrlResolver.js +3 -3
- package/dist/AzureUrlResolver.js.map +1 -1
- package/dist/interfaces.d.ts +56 -9
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/utils.d.ts +14 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +18 -0
- package/dist/utils.js.map +1 -0
- package/lib/AzureClient.d.ts +22 -5
- package/lib/AzureClient.d.ts.map +1 -1
- package/lib/AzureClient.js +86 -19
- package/lib/AzureClient.js.map +1 -1
- package/lib/AzureUrlResolver.d.ts +1 -1
- package/lib/AzureUrlResolver.d.ts.map +1 -1
- package/lib/AzureUrlResolver.js +3 -3
- package/lib/AzureUrlResolver.js.map +1 -1
- package/lib/interfaces.d.ts +56 -9
- package/lib/interfaces.d.ts.map +1 -1
- package/lib/interfaces.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/utils.d.ts +14 -0
- package/lib/utils.d.ts.map +1 -0
- package/lib/utils.js +13 -0
- package/lib/utils.js.map +1 -0
- package/package.json +35 -19
- package/src/AzureClient.ts +142 -27
- package/src/AzureUrlResolver.ts +4 -5
- package/src/interfaces.ts +62 -9
- package/src/packageVersion.ts +1 -1
- package/src/utils.ts +21 -0
package/src/AzureClient.ts
CHANGED
|
@@ -23,7 +23,18 @@ import {
|
|
|
23
23
|
RootDataObject,
|
|
24
24
|
} from "@fluidframework/fluid-static";
|
|
25
25
|
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
SummaryType,
|
|
28
|
+
} from "@fluidframework/protocol-definitions";
|
|
29
|
+
|
|
30
|
+
import {
|
|
31
|
+
AzureClientProps,
|
|
32
|
+
AzureConnectionConfig,
|
|
33
|
+
AzureContainerServices,
|
|
34
|
+
AzureContainerVersion,
|
|
35
|
+
AzureGetVersionsOptions,
|
|
36
|
+
} from "./interfaces";
|
|
37
|
+
import { isAzureRemoteConnectionConfig } from "./utils";
|
|
27
38
|
import { AzureAudience } from "./AzureAudience";
|
|
28
39
|
import {
|
|
29
40
|
AzureUrlResolver,
|
|
@@ -33,7 +44,12 @@ import {
|
|
|
33
44
|
/**
|
|
34
45
|
* Strongly typed id for connecting to a local Azure Fluid Relay.
|
|
35
46
|
*/
|
|
36
|
-
|
|
47
|
+
const LOCAL_MODE_TENANT_ID = "local";
|
|
48
|
+
const getTenantId = (connectionProps: AzureConnectionConfig): string => {
|
|
49
|
+
return isAzureRemoteConnectionConfig(connectionProps) ? connectionProps.tenantId : LOCAL_MODE_TENANT_ID;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const MAX_VERSION_COUNT = 5;
|
|
37
53
|
|
|
38
54
|
/**
|
|
39
55
|
* AzureClient provides the ability to have a Fluid object backed by the Azure Fluid Relay or,
|
|
@@ -48,14 +64,15 @@ export class AzureClient {
|
|
|
48
64
|
* @param props - Properties for initializing a new AzureClient instance
|
|
49
65
|
*/
|
|
50
66
|
constructor(private readonly props: AzureClientProps) {
|
|
67
|
+
// remove trailing slash from URL if any
|
|
68
|
+
props.connection.endpoint = props.connection.endpoint.replace(/\/$/, "");
|
|
51
69
|
this.urlResolver = new AzureUrlResolver();
|
|
52
70
|
// The local service implementation differs from the Azure Fluid Relay in blob
|
|
53
71
|
// storage format. Azure Fluid Relay supports whole summary upload. Local currently does not.
|
|
54
|
-
const enableWholeSummaryUpload =
|
|
55
|
-
this.props.connection.tenantId !== LOCAL_MODE_TENANT_ID;
|
|
72
|
+
const enableWholeSummaryUpload = isAzureRemoteConnectionConfig(this.props.connection);
|
|
56
73
|
this.documentServiceFactory = new RouterliciousDocumentServiceFactory(
|
|
57
74
|
this.props.connection.tokenProvider,
|
|
58
|
-
{ enableWholeSummaryUpload },
|
|
75
|
+
{ enableWholeSummaryUpload, enableDiscovery: true },
|
|
59
76
|
);
|
|
60
77
|
}
|
|
61
78
|
|
|
@@ -77,29 +94,60 @@ export class AzureClient {
|
|
|
77
94
|
config: {},
|
|
78
95
|
});
|
|
79
96
|
|
|
80
|
-
const
|
|
97
|
+
const fluidContainer = await this.createFluidContainer(
|
|
81
98
|
container,
|
|
82
|
-
|
|
99
|
+
this.props.connection,
|
|
83
100
|
);
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
101
|
+
const services = this.getContainerServices(container);
|
|
102
|
+
return { container: fluidContainer, services };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Creates new detached container out of specific version of another container.
|
|
107
|
+
* @param id - Unique ID of the source container in Azure Fluid Relay.
|
|
108
|
+
* @param containerSchema - Container schema used to access data objects in the container.
|
|
109
|
+
* @param version - Unique version of the source container in Azure Fluid Relay.
|
|
110
|
+
* It defaults to latest version if parameter not provided.
|
|
111
|
+
* @returns New detached container instance along with associated services.
|
|
112
|
+
*/
|
|
113
|
+
public async copyContainer(
|
|
114
|
+
id: string,
|
|
115
|
+
containerSchema: ContainerSchema,
|
|
116
|
+
version?: AzureContainerVersion,
|
|
117
|
+
): Promise<{
|
|
118
|
+
container: IFluidContainer;
|
|
119
|
+
services: AzureContainerServices;
|
|
120
|
+
}> {
|
|
121
|
+
const loader = this.createLoader(containerSchema);
|
|
122
|
+
const url = new URL(this.props.connection.endpoint);
|
|
123
|
+
url.searchParams.append("storage", encodeURIComponent(this.props.connection.endpoint));
|
|
124
|
+
url.searchParams.append("tenantId", encodeURIComponent(getTenantId(this.props.connection)));
|
|
125
|
+
url.searchParams.append("containerId", encodeURIComponent(id));
|
|
126
|
+
const sourceContainer = await loader.resolve({ url: url.href });
|
|
127
|
+
|
|
128
|
+
if (sourceContainer.resolvedUrl === undefined) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
"Source container cannot resolve URL.",
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const documentService = await this.documentServiceFactory.createDocumentService(sourceContainer.resolvedUrl);
|
|
135
|
+
const storage = await documentService.connectToStorage();
|
|
136
|
+
const handle = {
|
|
137
|
+
type: SummaryType.Handle,
|
|
138
|
+
handleType: SummaryType.Tree,
|
|
139
|
+
handle: version?.id ?? "latest",
|
|
140
|
+
};
|
|
141
|
+
const tree = await storage.downloadSummary(handle);
|
|
142
|
+
|
|
143
|
+
const container = await loader.rehydrateDetachedContainerFromSnapshot(
|
|
144
|
+
JSON.stringify(tree),
|
|
88
145
|
);
|
|
89
|
-
const fluidContainer = new (class extends FluidContainer {
|
|
90
|
-
async attach() {
|
|
91
|
-
if (this.attachState !== AttachState.Detached) {
|
|
92
|
-
throw new Error(
|
|
93
|
-
"Cannot attach container. Container is not in detached state",
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
await container.attach(createNewRequest);
|
|
97
|
-
const resolved = container.resolvedUrl;
|
|
98
|
-
ensureFluidResolvedUrl(resolved);
|
|
99
|
-
return resolved.id;
|
|
100
|
-
}
|
|
101
|
-
})(container, rootDataObject);
|
|
102
146
|
|
|
147
|
+
const fluidContainer = await this.createFluidContainer(
|
|
148
|
+
container,
|
|
149
|
+
this.props.connection,
|
|
150
|
+
);
|
|
103
151
|
const services = this.getContainerServices(container);
|
|
104
152
|
return { container: fluidContainer, services };
|
|
105
153
|
}
|
|
@@ -118,9 +166,9 @@ export class AzureClient {
|
|
|
118
166
|
services: AzureContainerServices;
|
|
119
167
|
}> {
|
|
120
168
|
const loader = this.createLoader(containerSchema);
|
|
121
|
-
const url = new URL(this.props.connection.
|
|
122
|
-
url.searchParams.append("storage", encodeURIComponent(this.props.connection.
|
|
123
|
-
url.searchParams.append("tenantId", encodeURIComponent(this.props.connection
|
|
169
|
+
const url = new URL(this.props.connection.endpoint);
|
|
170
|
+
url.searchParams.append("storage", encodeURIComponent(this.props.connection.endpoint));
|
|
171
|
+
url.searchParams.append("tenantId", encodeURIComponent(getTenantId(this.props.connection)));
|
|
124
172
|
url.searchParams.append("containerId", encodeURIComponent(id));
|
|
125
173
|
const container = await loader.resolve({ url: url.href });
|
|
126
174
|
const rootDataObject = await requestFluidObject<RootDataObject>(
|
|
@@ -132,6 +180,44 @@ export class AzureClient {
|
|
|
132
180
|
return { container: fluidContainer, services };
|
|
133
181
|
}
|
|
134
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Get the list of versions for specific container.
|
|
185
|
+
* @param id - Unique ID of the source container in Azure Fluid Relay.
|
|
186
|
+
* @param options - "Get" options. If options are not provided, API
|
|
187
|
+
* will assume maxCount of versions to retreive to be 5.
|
|
188
|
+
* @returns Array of available container versions.
|
|
189
|
+
*/
|
|
190
|
+
public async getContainerVersions(
|
|
191
|
+
id: string,
|
|
192
|
+
options?: AzureGetVersionsOptions,
|
|
193
|
+
): Promise<AzureContainerVersion[]> {
|
|
194
|
+
const url = new URL(this.props.connection.endpoint);
|
|
195
|
+
url.searchParams.append(
|
|
196
|
+
"storage",
|
|
197
|
+
encodeURIComponent(this.props.connection.endpoint),
|
|
198
|
+
);
|
|
199
|
+
url.searchParams.append(
|
|
200
|
+
"tenantId",
|
|
201
|
+
encodeURIComponent(getTenantId(this.props.connection)),
|
|
202
|
+
);
|
|
203
|
+
url.searchParams.append("containerId", encodeURIComponent(id));
|
|
204
|
+
|
|
205
|
+
const resolvedUrl = await this.urlResolver.resolve({ url: url.href });
|
|
206
|
+
if (!resolvedUrl) {
|
|
207
|
+
throw new Error("Unable to resolved URL");
|
|
208
|
+
}
|
|
209
|
+
const documentService =
|
|
210
|
+
await this.documentServiceFactory.createDocumentService(
|
|
211
|
+
resolvedUrl,
|
|
212
|
+
);
|
|
213
|
+
const storage = await documentService.connectToStorage();
|
|
214
|
+
const versions = await storage.getVersions(null, options?.maxCount ?? MAX_VERSION_COUNT);
|
|
215
|
+
|
|
216
|
+
return versions.map((item) => {
|
|
217
|
+
return { id: item.id, date: item.date };
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
135
221
|
private getContainerServices(container: IContainer): AzureContainerServices {
|
|
136
222
|
return {
|
|
137
223
|
audience: new AzureAudience(container),
|
|
@@ -157,4 +243,33 @@ export class AzureClient {
|
|
|
157
243
|
logger: this.props.logger,
|
|
158
244
|
});
|
|
159
245
|
}
|
|
246
|
+
|
|
247
|
+
private async createFluidContainer(
|
|
248
|
+
container: IContainer,
|
|
249
|
+
connection: AzureConnectionConfig,
|
|
250
|
+
): Promise<FluidContainer> {
|
|
251
|
+
const createNewRequest = createAzureCreateNewRequest(
|
|
252
|
+
connection.endpoint,
|
|
253
|
+
getTenantId(connection),
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
const rootDataObject = await requestFluidObject<RootDataObject>(
|
|
257
|
+
container,
|
|
258
|
+
"/",
|
|
259
|
+
);
|
|
260
|
+
return new (class extends FluidContainer {
|
|
261
|
+
async attach() {
|
|
262
|
+
if (this.attachState !== AttachState.Detached) {
|
|
263
|
+
throw new Error(
|
|
264
|
+
"Cannot attach container. Container is not in detached state",
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
await container.attach(createNewRequest);
|
|
268
|
+
const resolved = container.resolvedUrl;
|
|
269
|
+
ensureFluidResolvedUrl(resolved);
|
|
270
|
+
return resolved.id;
|
|
271
|
+
}
|
|
272
|
+
})(container, rootDataObject);
|
|
273
|
+
}
|
|
274
|
+
// #endregion
|
|
160
275
|
}
|
package/src/AzureUrlResolver.ts
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
// InsecureTokenProvider for basic scenarios or more robust, secure providers that fulfill the
|
|
17
17
|
// ITokenProvider interface
|
|
18
18
|
export class AzureUrlResolver implements IUrlResolver {
|
|
19
|
-
constructor() {}
|
|
19
|
+
constructor() { }
|
|
20
20
|
|
|
21
21
|
public async resolve(request: IRequest): Promise<IFluidResolvedUrl> {
|
|
22
22
|
const { ordererUrl, storageUrl, tenantId, containerId } = decodeAzureUrl(
|
|
@@ -100,12 +100,11 @@ function decodeAzureUrl(urlString: string): {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
export const createAzureCreateNewRequest = (
|
|
103
|
-
|
|
104
|
-
storageUrl: string,
|
|
103
|
+
endpointUrl: string,
|
|
105
104
|
tenantId: string,
|
|
106
105
|
): IRequest => {
|
|
107
|
-
const url = new URL(
|
|
108
|
-
url.searchParams.append("storage", encodeURIComponent(
|
|
106
|
+
const url = new URL(endpointUrl);
|
|
107
|
+
url.searchParams.append("storage", encodeURIComponent(endpointUrl));
|
|
109
108
|
url.searchParams.append("tenantId", encodeURIComponent(tenantId));
|
|
110
109
|
return {
|
|
111
110
|
url: url.href,
|
package/src/interfaces.ts
CHANGED
|
@@ -30,28 +30,81 @@ export interface AzureClientProps {
|
|
|
30
30
|
readonly logger?: ITelemetryBaseLogger;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Container version metadata.
|
|
35
|
+
*/
|
|
36
|
+
export interface AzureContainerVersion {
|
|
37
|
+
/**
|
|
38
|
+
* Version ID
|
|
39
|
+
*/
|
|
40
|
+
id: string;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Time when version was generated.
|
|
44
|
+
* ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ
|
|
45
|
+
*/
|
|
46
|
+
date?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Options for "Get Container Versions" API.
|
|
51
|
+
*/
|
|
52
|
+
export interface AzureGetVersionsOptions {
|
|
53
|
+
/**
|
|
54
|
+
* Max number of versions
|
|
55
|
+
*/
|
|
56
|
+
maxCount: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The type of connection.
|
|
61
|
+
* - "local" for local connections to a Fluid relay instance running on the localhost
|
|
62
|
+
* - "remote" for client connections to the Azure Fluid Relay service
|
|
63
|
+
*/
|
|
64
|
+
export type AzureConnectionConfigType = "local" | "remote";
|
|
65
|
+
|
|
33
66
|
/**
|
|
34
67
|
* Parameters for establishing a connection with the Azure Fluid Relay.
|
|
35
68
|
*/
|
|
36
69
|
export interface AzureConnectionConfig {
|
|
37
70
|
/**
|
|
38
|
-
*
|
|
71
|
+
* The type of connection. Whether we're connecting to a remote Fluid relay server or a local instance.
|
|
39
72
|
*/
|
|
40
|
-
|
|
73
|
+
type: AzureConnectionConfigType;
|
|
41
74
|
/**
|
|
42
|
-
* URI to the Azure Fluid Relay
|
|
75
|
+
* URI to the Azure Fluid Relay service discovery endpoint.
|
|
43
76
|
*/
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Unique tenant identifier
|
|
47
|
-
*/
|
|
48
|
-
tenantId: "local" | string;
|
|
77
|
+
endpoint: string;
|
|
49
78
|
/**
|
|
50
|
-
* Instance that provides Azure Fluid Relay endpoint tokens
|
|
79
|
+
* Instance that provides Azure Fluid Relay endpoint tokens.
|
|
51
80
|
*/
|
|
52
81
|
tokenProvider: ITokenProvider;
|
|
53
82
|
}
|
|
54
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Parameters for establishing a remote connection with the Azure Fluid Relay.
|
|
86
|
+
*/
|
|
87
|
+
export interface AzureRemoteConnectionConfig extends AzureConnectionConfig {
|
|
88
|
+
/**
|
|
89
|
+
* The type of connection. Set to a remote connection.
|
|
90
|
+
*/
|
|
91
|
+
type: "remote";
|
|
92
|
+
/**
|
|
93
|
+
* Unique tenant identifier.
|
|
94
|
+
*/
|
|
95
|
+
tenantId: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Parameters for establishing a local connection with a local instance of the Azure Fluid Relay.
|
|
100
|
+
*/
|
|
101
|
+
export interface AzureLocalConnectionConfig extends AzureConnectionConfig {
|
|
102
|
+
/**
|
|
103
|
+
* The type of connection. Set to a remote connection.
|
|
104
|
+
*/
|
|
105
|
+
type: "local";
|
|
106
|
+
}
|
|
107
|
+
|
|
55
108
|
/**
|
|
56
109
|
* AzureContainerServices is returned by the AzureClient alongside a FluidContainer.
|
|
57
110
|
* It holds the functionality specifically tied to the Azure Fluid Relay, and how the data stored in
|
package/src/packageVersion.ts
CHANGED
package/src/utils.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
import { AzureRemoteConnectionConfig, AzureLocalConnectionConfig, AzureConnectionConfig } from "./interfaces";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Type guard for validating a given AzureConnectionConfig is a remote connection type (AzureRemoteConnectionConfig)
|
|
9
|
+
*/
|
|
10
|
+
export function isAzureRemoteConnectionConfig(
|
|
11
|
+
connectionConfig: AzureConnectionConfig): connectionConfig is AzureRemoteConnectionConfig {
|
|
12
|
+
return connectionConfig.type === "remote";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Type guard for validating a given AzureConnectionConfig is a local connection type (AzureLocalConnectionConfig)
|
|
17
|
+
*/
|
|
18
|
+
export function isAzureLocalConnectionConfig(
|
|
19
|
+
connectionConfig: AzureConnectionConfig): connectionConfig is AzureLocalConnectionConfig {
|
|
20
|
+
return connectionConfig.type === "local";
|
|
21
|
+
}
|