@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.
Files changed (46) hide show
  1. package/.eslintrc.js +1 -1
  2. package/README.md +7 -8
  3. package/dist/AzureClient.d.ts +22 -5
  4. package/dist/AzureClient.d.ts.map +1 -1
  5. package/dist/AzureClient.js +87 -20
  6. package/dist/AzureClient.js.map +1 -1
  7. package/dist/AzureUrlResolver.d.ts +1 -1
  8. package/dist/AzureUrlResolver.d.ts.map +1 -1
  9. package/dist/AzureUrlResolver.js +3 -3
  10. package/dist/AzureUrlResolver.js.map +1 -1
  11. package/dist/interfaces.d.ts +56 -9
  12. package/dist/interfaces.d.ts.map +1 -1
  13. package/dist/interfaces.js.map +1 -1
  14. package/dist/packageVersion.d.ts +1 -1
  15. package/dist/packageVersion.d.ts.map +1 -1
  16. package/dist/packageVersion.js +1 -1
  17. package/dist/packageVersion.js.map +1 -1
  18. package/dist/utils.d.ts +14 -0
  19. package/dist/utils.d.ts.map +1 -0
  20. package/dist/utils.js +18 -0
  21. package/dist/utils.js.map +1 -0
  22. package/lib/AzureClient.d.ts +22 -5
  23. package/lib/AzureClient.d.ts.map +1 -1
  24. package/lib/AzureClient.js +86 -19
  25. package/lib/AzureClient.js.map +1 -1
  26. package/lib/AzureUrlResolver.d.ts +1 -1
  27. package/lib/AzureUrlResolver.d.ts.map +1 -1
  28. package/lib/AzureUrlResolver.js +3 -3
  29. package/lib/AzureUrlResolver.js.map +1 -1
  30. package/lib/interfaces.d.ts +56 -9
  31. package/lib/interfaces.d.ts.map +1 -1
  32. package/lib/interfaces.js.map +1 -1
  33. package/lib/packageVersion.d.ts +1 -1
  34. package/lib/packageVersion.d.ts.map +1 -1
  35. package/lib/packageVersion.js +1 -1
  36. package/lib/packageVersion.js.map +1 -1
  37. package/lib/utils.d.ts +14 -0
  38. package/lib/utils.d.ts.map +1 -0
  39. package/lib/utils.js +13 -0
  40. package/lib/utils.js.map +1 -0
  41. package/package.json +35 -19
  42. package/src/AzureClient.ts +142 -27
  43. package/src/AzureUrlResolver.ts +4 -5
  44. package/src/interfaces.ts +62 -9
  45. package/src/packageVersion.ts +1 -1
  46. package/src/utils.ts +21 -0
@@ -23,7 +23,18 @@ import {
23
23
  RootDataObject,
24
24
  } from "@fluidframework/fluid-static";
25
25
 
26
- import { AzureClientProps, AzureContainerServices } from "./interfaces";
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
- export const LOCAL_MODE_TENANT_ID = "local";
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 rootDataObject = await requestFluidObject<RootDataObject>(
97
+ const fluidContainer = await this.createFluidContainer(
81
98
  container,
82
- "/",
99
+ this.props.connection,
83
100
  );
84
- const createNewRequest = createAzureCreateNewRequest(
85
- this.props.connection.orderer,
86
- this.props.connection.storage,
87
- this.props.connection.tenantId,
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.orderer);
122
- url.searchParams.append("storage", encodeURIComponent(this.props.connection.storage));
123
- url.searchParams.append("tenantId", encodeURIComponent(this.props.connection.tenantId));
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
  }
@@ -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
- ordererUrl: string,
104
- storageUrl: string,
103
+ endpointUrl: string,
105
104
  tenantId: string,
106
105
  ): IRequest => {
107
- const url = new URL(ordererUrl);
108
- url.searchParams.append("storage", encodeURIComponent(storageUrl));
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
- * URI to the Azure Fluid Relay orderer endpoint
71
+ * The type of connection. Whether we're connecting to a remote Fluid relay server or a local instance.
39
72
  */
40
- orderer: string;
73
+ type: AzureConnectionConfigType;
41
74
  /**
42
- * URI to the Azure Fluid Relay storage endpoint
75
+ * URI to the Azure Fluid Relay service discovery endpoint.
43
76
  */
44
- storage: string;
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
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/azure-client";
9
- export const pkgVersion = "0.59.3000";
9
+ export const pkgVersion = "1.0.0";
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
+ }