Skip to content

Commit aee01c3

Browse files
committed
first attempt
1 parent 51465ce commit aee01c3

File tree

5 files changed

+999
-1
lines changed

5 files changed

+999
-1
lines changed

packages/firestore/src/lite-api/snapshot.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
UntypedFirestoreDataConverter
3939
} from './user_data_reader';
4040
import { AbstractUserDataWriter } from './user_data_writer';
41+
import { Timestamp } from '../lite-api/timestamp';
4142

4243
/**
4344
* Converter used by `withConverter()` to transform user objects of type
@@ -329,6 +330,18 @@ export class DocumentSnapshot<
329330
);
330331
}
331332

333+
get createTime(): Timestamp | undefined {
334+
return this._document?.createTime.toTimestamp();
335+
}
336+
337+
get updateTime(): Timestamp | undefined {
338+
return this._document?.updateTime.toTimestamp();
339+
}
340+
341+
get readTime(): Timestamp | undefined {
342+
return this._document?.readTime.toTimestamp();
343+
}
344+
332345
/**
333346
* Signals whether or not the document at the snapshot's location exists.
334347
*

packages/firestore/src/model/path.ts

Lines changed: 204 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,22 @@ import { Code, FirestoreError } from '../util/error';
2222

2323
export const DOCUMENT_KEY_NAME = '__name__';
2424

25+
/*!
26+
* A regular expression to verify an absolute Resource Path in Firestore. It
27+
* extracts the project ID, the database name and the relative resource path
28+
* if available.
29+
*
30+
* @type {RegExp}
31+
*/
32+
const RESOURCE_PATH_RE =
33+
// Note: [\s\S] matches all characters including newlines.
34+
/^projects\/([^/]*)\/databases\/([^/]*)(?:\/documents\/)?([\s\S]*)$/;
35+
2536
/**
2637
* Path represents an ordered sequence of string segments.
2738
*/
2839
abstract class BasePath<B extends BasePath<B>> {
29-
private segments: string[];
40+
protected segments: string[];
3041
private offset: number;
3142
private len: number;
3243

@@ -257,6 +268,21 @@ export class ResourcePath extends BasePath<ResourcePath> {
257268
return this.toArray().map(encodeURIComponent).join('/');
258269
}
259270

271+
/**
272+
* Converts this path to a fully qualified ResourcePath.
273+
*
274+
* @private
275+
* @internal
276+
* @param projectId The project ID of the current Firestore project.
277+
* @return A fully-qualified resource path pointing to the same element.
278+
*/
279+
toQualifiedResourcePath(
280+
projectId: string,
281+
databaseId: string
282+
): QualifiedResourcePath {
283+
return new QualifiedResourcePath(projectId, databaseId, ...this.segments);
284+
}
285+
260286
/**
261287
* Creates a resource path from the given slash-delimited string. If multiple
262288
* arguments are provided, all components are combined. Leading and trailing
@@ -287,6 +313,183 @@ export class ResourcePath extends BasePath<ResourcePath> {
287313
}
288314
}
289315

316+
/**
317+
* A slash-separated path that includes a project and database ID for referring
318+
* to resources in any Firestore project.
319+
*
320+
* @private
321+
* @internal
322+
*/
323+
export class QualifiedResourcePath extends ResourcePath {
324+
/**
325+
* The project ID of this path.
326+
*/
327+
readonly projectId: string;
328+
329+
/**
330+
* The database ID of this path.
331+
*/
332+
readonly databaseId: string;
333+
334+
/**
335+
* Constructs a Firestore Resource Path.
336+
*
337+
* @private
338+
* @internal
339+
* @param projectId The Firestore project id.
340+
* @param databaseId The Firestore database id.
341+
* @param segments Sequence of names of the parts of the path.
342+
*/
343+
constructor(projectId: string, databaseId: string, ...segments: string[]) {
344+
super(segments);
345+
346+
this.projectId = projectId;
347+
this.databaseId = databaseId;
348+
}
349+
350+
/**
351+
* String representation of the path relative to the database root.
352+
* @private
353+
* @internal
354+
*/
355+
get relativeName(): string {
356+
return this.segments.join('/');
357+
}
358+
359+
/**
360+
* Creates a resource path from an absolute Firestore path.
361+
*
362+
* @private
363+
* @internal
364+
* @param absolutePath A string representation of a Resource Path.
365+
* @returns The new ResourcePath.
366+
*/
367+
static fromSlashSeparatedString(absolutePath: string): QualifiedResourcePath {
368+
const elements = RESOURCE_PATH_RE.exec(absolutePath);
369+
370+
if (elements) {
371+
const project = elements[1];
372+
const database = elements[2];
373+
const path = elements[3];
374+
return new QualifiedResourcePath(project, database).append(path);
375+
}
376+
377+
throw new Error(`Resource name '${absolutePath}' is not valid.`);
378+
}
379+
380+
/**
381+
* Create a child path beneath the current level.
382+
*
383+
* @private
384+
* @internal
385+
* @param relativePath Relative path to append to the current path.
386+
* @returns The new path.
387+
*/
388+
append(relativePath: ResourcePath | string): QualifiedResourcePath {
389+
// `super.append()` calls `QualifiedResourcePath.construct()` when invoked
390+
// from here and returns a QualifiedResourcePath.
391+
return super.append(relativePath) as QualifiedResourcePath;
392+
}
393+
394+
/**
395+
* Create a child path beneath the current level.
396+
*
397+
* @private
398+
* @internal
399+
* @returns The new path.
400+
*/
401+
parent(): QualifiedResourcePath | null {
402+
return super.parent() as QualifiedResourcePath | null;
403+
}
404+
405+
/**
406+
* String representation of a ResourcePath as expected by the API.
407+
*
408+
* @private
409+
* @internal
410+
* @returns The representation as expected by the API.
411+
*/
412+
get formattedName(): string {
413+
const components = [
414+
'projects',
415+
this.projectId,
416+
'databases',
417+
this.databaseId,
418+
'documents',
419+
...this.segments
420+
];
421+
return components.join('/');
422+
}
423+
424+
/**
425+
* Constructs a new instance of ResourcePath. We need this instead of using
426+
* the normal constructor because polymorphic 'this' doesn't work on static
427+
* methods.
428+
*
429+
* @private
430+
* @internal
431+
* @param segments Sequence of names of the parts of the path.
432+
* @returns The newly created QualifiedResourcePath.
433+
*/
434+
construct(segments: string[]): QualifiedResourcePath {
435+
return new QualifiedResourcePath(
436+
this.projectId,
437+
this.databaseId,
438+
...segments
439+
);
440+
}
441+
442+
/**
443+
* Convenience method to match the ResourcePath API. This method always
444+
* returns the current instance.
445+
*
446+
* @private
447+
* @internal
448+
*/
449+
toQualifiedResourcePath(): QualifiedResourcePath {
450+
return this;
451+
}
452+
453+
/**
454+
* Compare the current path against another ResourcePath object.
455+
*
456+
* @private
457+
* @internal
458+
* @param other The path to compare to.
459+
* @returns -1 if current < other, 1 if current > other, 0 if equal
460+
*/
461+
compareTo(other: ResourcePath): number {
462+
if (other instanceof QualifiedResourcePath) {
463+
if (this.projectId < other.projectId) {
464+
return -1;
465+
}
466+
if (this.projectId > other.projectId) {
467+
return 1;
468+
}
469+
470+
if (this.databaseId < other.databaseId) {
471+
return -1;
472+
}
473+
if (this.databaseId > other.databaseId) {
474+
return 1;
475+
}
476+
}
477+
478+
return super.compareTo(other);
479+
}
480+
481+
/**
482+
* Converts this ResourcePath to the Firestore Proto representation.
483+
* @private
484+
* @internal
485+
*/
486+
toProto(): api.IValue {
487+
return {
488+
referenceValue: this.formattedName
489+
};
490+
}
491+
}
492+
290493
const identifierRegExp = /^[_a-zA-Z][_a-zA-Z0-9]*$/;
291494

292495
/**
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
19+
import {
20+
BundleElement,
21+
BundleMetadata
22+
} from '../protos/firestore_bundle_proto';
23+
import { JsonProtoSerializer } from '../remote/serializer';
24+
import { SizedBundleElement } from '../util/bundle_reader';
25+
26+
27+
/**
28+
* A class that can serialize Firestore results into a Firestore bundle.
29+
*
30+
*/
31+
export interface BundleBuilder {
32+
serializer: JsonProtoSerializer;
33+
34+
close(): Promise<void>;
35+
36+
getMetadata(): Promise<BundleMetadata>;
37+
38+
nextElement(): Promise<SizedBundleElement | null>;
39+
}

0 commit comments

Comments
 (0)