@umituz/react-native-firebase 2.6.1 → 2.6.3
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/package.json +1 -1
- package/src/application/auth/ports/AuthPort_part_aa +150 -0
- package/src/application/auth/ports/AuthPort_part_ab +14 -0
- package/src/application/auth/use-cases/SignInUseCaseHelpers.ts +0 -0
- package/src/application/auth/use-cases/SignInUseCaseMain.ts +0 -0
- package/src/application/auth/use-cases/SignInUseCase_part_aa +150 -0
- package/src/application/auth/use-cases/SignInUseCase_part_ab +103 -0
- package/src/application/auth/use-cases/SignOutUseCaseCleanup.ts +0 -0
- package/src/application/auth/use-cases/SignOutUseCaseMain.ts +0 -0
- package/src/application/auth/use-cases/SignOutUseCase_part_aa +150 -0
- package/src/application/auth/use-cases/SignOutUseCase_part_ab +138 -0
- package/src/domains/account-deletion/domain/services/UserValidationHelpers.ts.bak +181 -0
- package/src/domains/account-deletion/domain/services/UserValidationHelpers_part_aa +150 -0
- package/src/domains/account-deletion/domain/services/UserValidationHelpers_part_ab +31 -0
- package/src/domains/account-deletion/domain/services/{UserValidationService.ts → UserValidationService.ts.bak} +1 -10
- package/src/domains/account-deletion/domain/services/UserValidationService_part_aa +150 -0
- package/src/domains/account-deletion/domain/services/UserValidationService_part_ab +136 -0
- package/src/domains/account-deletion/infrastructure/services/AccountDeletionExecutor_part_aa +150 -0
- package/src/domains/account-deletion/infrastructure/services/AccountDeletionExecutor_part_ab +80 -0
- package/src/domains/account-deletion/infrastructure/services/AccountDeletionReauthHandler_part_aa +150 -0
- package/src/domains/account-deletion/infrastructure/services/AccountDeletionReauthHandler_part_ab +24 -0
- package/src/domains/account-deletion/infrastructure/services/AccountDeletionRepository_part_aa +150 -0
- package/src/domains/account-deletion/infrastructure/services/AccountDeletionRepository_part_ab +116 -0
- package/src/domains/account-deletion/infrastructure/services/reauthentication.service_part_aa +150 -0
- package/src/domains/account-deletion/infrastructure/services/reauthentication.service_part_ab +10 -0
- package/src/domains/auth/infrastructure_part_aa +150 -0
- package/src/domains/auth/infrastructure_part_ab +6 -0
- package/src/domains/auth/presentation/hooks/GoogleOAuthHelpers.ts +0 -0
- package/src/domains/auth/presentation/hooks/GoogleOAuthHookService_part_aa +150 -0
- package/src/domains/auth/presentation/hooks/GoogleOAuthHookService_part_ab +97 -0
- package/src/domains/auth/presentation/hooks/GoogleOAuthService.ts +0 -0
- package/src/domains/firestore/domain/entities/Collection.ts +31 -191
- package/src/domains/firestore/domain/entities/Collection.ts.bak +288 -0
- package/src/domains/firestore/domain/entities/CollectionFactory.ts +55 -0
- package/src/domains/firestore/domain/entities/CollectionHelpers.ts +143 -0
- package/src/domains/firestore/domain/entities/CollectionUtils.ts +72 -0
- package/src/domains/firestore/domain/entities/CollectionValidation.ts +138 -0
- package/src/domains/firestore/domain/entities/Collection_part_aa +150 -0
- package/src/domains/firestore/domain/entities/Collection_part_ab +138 -0
- package/src/domains/firestore/domain/entities/DocumentHelpers.ts +0 -0
- package/src/domains/firestore/domain/entities/DocumentMain.ts +0 -0
- package/src/domains/firestore/domain/entities/Document_part_aa +150 -0
- package/src/domains/firestore/domain/entities/Document_part_ab +83 -0
- package/src/domains/firestore/domain/index.ts +35 -8
- package/src/domains/firestore/domain/services/QueryServiceAnalysis_part_aa +150 -0
- package/src/domains/firestore/domain/services/QueryServiceAnalysis_part_ab +19 -0
- package/src/domains/firestore/domain/services/QueryServiceHelpers_part_aa +150 -0
- package/src/domains/firestore/domain/services/QueryServiceHelpers_part_ab +1 -0
- package/src/domains/firestore/domain/services/QueryService_part_aa +150 -0
- package/src/domains/firestore/domain/services/QueryService_part_ab +32 -0
- package/src/domains/firestore/domain/value-objects/QueryOptions.ts +20 -68
- package/src/domains/firestore/domain/value-objects/QueryOptions.ts.bak +6 -135
- package/src/domains/firestore/domain/value-objects/QueryOptionsFactory.ts +95 -0
- package/src/domains/firestore/domain/value-objects/QueryOptionsHelpers.ts +110 -0
- package/src/domains/firestore/domain/value-objects/QueryOptionsSerialization_part_aa +150 -0
- package/src/domains/firestore/domain/value-objects/QueryOptionsSerialization_part_ab +57 -0
- package/src/domains/firestore/domain/value-objects/QueryOptionsValidation_part_aa +150 -0
- package/src/domains/firestore/domain/value-objects/QueryOptionsValidation_part_ab +32 -0
- package/src/domains/firestore/domain/value-objects/QueryOptions_part_aa +150 -0
- package/src/domains/firestore/domain/value-objects/QueryOptions_part_ab +41 -0
- package/src/domains/firestore/domain/value-objects/WhereClause.ts +35 -205
- package/src/domains/firestore/domain/value-objects/WhereClause.ts.bak +299 -0
- package/src/domains/firestore/domain/value-objects/WhereClauseFactory.ts +44 -150
- package/src/domains/firestore/domain/value-objects/WhereClauseFactory.ts.bak +207 -0
- package/src/domains/firestore/domain/value-objects/WhereClauseFactory_part_aa +150 -0
- package/src/domains/firestore/domain/value-objects/WhereClauseFactory_part_ab +57 -0
- package/src/domains/firestore/domain/value-objects/WhereClauseHelpers.ts +123 -0
- package/src/domains/firestore/domain/value-objects/WhereClauseValidation.ts +83 -0
- package/src/domains/firestore/domain/value-objects/WhereClause_part_aa +150 -0
- package/src/domains/firestore/domain/value-objects/WhereClause_part_ab +149 -0
- package/src/shared/infrastructure/base/ErrorHandler_part_aa +150 -0
- package/src/shared/infrastructure/base/ErrorHandler_part_ab +39 -0
- package/src/shared/infrastructure/base/ServiceBase_part_aa +150 -0
- package/src/shared/infrastructure/base/ServiceBase_part_ab +70 -0
- package/src/shared/infrastructure/config/base/ServiceClientSingleton_part_aa +150 -0
- package/src/shared/infrastructure/config/base/ServiceClientSingleton_part_ab +5 -0
- /package/src/application/auth/ports/{AuthPort.ts → AuthPort.ts.bak} +0 -0
- /package/src/application/auth/use-cases/{SignInUseCase.ts → SignInUseCase.ts.bak} +0 -0
- /package/src/application/auth/use-cases/{SignOutUseCase.ts → SignOutUseCase.ts.bak} +0 -0
- /package/src/domains/account-deletion/infrastructure/services/{AccountDeletionExecutor.ts → AccountDeletionExecutor.ts.bak} +0 -0
- /package/src/domains/account-deletion/infrastructure/services/{AccountDeletionReauthHandler.ts → AccountDeletionReauthHandler.ts.bak} +0 -0
- /package/src/domains/account-deletion/infrastructure/services/{AccountDeletionRepository.ts → AccountDeletionRepository.ts.bak} +0 -0
- /package/src/domains/account-deletion/infrastructure/services/{reauthentication.service.ts → reauthentication.service.ts.bak} +0 -0
- /package/src/domains/auth/{infrastructure.ts → infrastructure.ts.bak} +0 -0
- /package/src/domains/auth/presentation/hooks/{GoogleOAuthHookService.ts → GoogleOAuthHookService.ts.bak} +0 -0
- /package/src/domains/firestore/domain/entities/{Document.ts → Document.ts.bak} +0 -0
- /package/src/domains/firestore/domain/services/{QueryService.ts → QueryService.ts.bak} +0 -0
- /package/src/domains/firestore/domain/services/{QueryServiceAnalysis.ts → QueryServiceAnalysis.ts.bak} +0 -0
- /package/src/domains/firestore/domain/services/{QueryServiceHelpers.ts → QueryServiceHelpers.ts.bak} +0 -0
- /package/src/domains/firestore/domain/value-objects/{QueryOptionsSerialization.ts → QueryOptionsSerialization.ts.bak} +0 -0
- /package/src/domains/firestore/domain/value-objects/{QueryOptionsValidation.ts → QueryOptionsValidation.ts.bak} +0 -0
- /package/src/shared/infrastructure/base/{ErrorHandler.ts → ErrorHandler.ts.bak} +0 -0
- /package/src/shared/infrastructure/base/{ServiceBase.ts → ServiceBase.ts.bak} +0 -0
- /package/src/shared/infrastructure/config/base/{ServiceClientSingleton.ts → ServiceClientSingleton.ts.bak} +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query Service (Main Builder)
|
|
3
|
+
* Single Responsibility: Build and validate Firestore queries
|
|
4
|
+
*
|
|
5
|
+
* Domain service that encapsulates query building logic.
|
|
6
|
+
* Moves business logic from infrastructure to domain layer.
|
|
7
|
+
*
|
|
8
|
+
* Max lines: 150 (enforced for maintainability)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Query, Firestore, WhereFilterOp } from 'firebase/firestore';
|
|
12
|
+
import { collection, query, where, orderBy, limit, startAfter, startAt } from 'firebase/firestore';
|
|
13
|
+
import { QueryOptions, createQueryOptions } from '../value-objects/QueryOptions';
|
|
14
|
+
import { WhereClause } from '../value-objects/WhereClause';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Query builder result
|
|
18
|
+
*/
|
|
19
|
+
export interface QueryBuilderResult {
|
|
20
|
+
readonly query: Query;
|
|
21
|
+
readonly options: QueryOptions;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Query service
|
|
26
|
+
* Provides query building and validation functionality
|
|
27
|
+
*/
|
|
28
|
+
export class QueryService {
|
|
29
|
+
private readonly db: Firestore;
|
|
30
|
+
|
|
31
|
+
constructor(db: Firestore) {
|
|
32
|
+
this.db = db;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Build a query from options
|
|
37
|
+
* Main method for query construction
|
|
38
|
+
*/
|
|
39
|
+
buildQuery(collectionPath: string, options: QueryOptions): QueryBuilderResult {
|
|
40
|
+
const validation = options.validate();
|
|
41
|
+
if (!validation.valid) {
|
|
42
|
+
throw new Error(`Invalid query options: ${validation.errors.join(', ')}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const collectionRef = collection(this.db, collectionPath);
|
|
46
|
+
let q: Query = collectionRef;
|
|
47
|
+
|
|
48
|
+
// Apply where clauses
|
|
49
|
+
for (const clause of options.whereClauses) {
|
|
50
|
+
q = query(q, where(clause.field, clause.operator, clause.value));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Apply sort options
|
|
54
|
+
for (const sort of options.sortOptions) {
|
|
55
|
+
q = query(q, orderBy(sort.field, sort.direction));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Apply date range
|
|
59
|
+
if (options.dateRange) {
|
|
60
|
+
const { field, startDate, endDate } = options.dateRange;
|
|
61
|
+
if (startDate) {
|
|
62
|
+
q = query(q, where(field, '>=', startDate));
|
|
63
|
+
}
|
|
64
|
+
if (endDate) {
|
|
65
|
+
q = query(q, where(field, '<=', endDate));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Apply pagination
|
|
70
|
+
if (options.pagination) {
|
|
71
|
+
const { cursor, limit: limitValue, startAfter: startAfterValue, startAt: startAtValue } = options.pagination;
|
|
72
|
+
|
|
73
|
+
if (startAfterValue !== undefined) {
|
|
74
|
+
q = query(q, startAfter(startAfterValue));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (startAtValue !== undefined) {
|
|
78
|
+
q = query(q, startAt(startAtValue));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (limitValue !== undefined) {
|
|
82
|
+
q = query(q, limit(limitValue));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return { query: q, options };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Build simple query with single where clause
|
|
91
|
+
*/
|
|
92
|
+
buildSimpleQuery(
|
|
93
|
+
collectionPath: string,
|
|
94
|
+
field: string,
|
|
95
|
+
operator: WhereFilterOp,
|
|
96
|
+
value: unknown
|
|
97
|
+
): Query {
|
|
98
|
+
const options = createQueryOptions({
|
|
99
|
+
where: [WhereClause.create(field, operator, value)],
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return this.buildQuery(collectionPath, options).query;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Build query with equality filter
|
|
107
|
+
*/
|
|
108
|
+
buildEqualsQuery(collectionPath: string, field: string, value: unknown): Query {
|
|
109
|
+
return this.buildSimpleQuery(collectionPath, field, '==', value);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Build query with multiple equality filters
|
|
114
|
+
*/
|
|
115
|
+
buildMultiEqualsQuery(
|
|
116
|
+
collectionPath: string,
|
|
117
|
+
filters: Record<string, unknown>
|
|
118
|
+
): Query {
|
|
119
|
+
const whereClauses = Object.entries(filters).map(([field, value]) =>
|
|
120
|
+
WhereClause.equals(field, value)
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const options = createQueryOptions({ where: whereClauses });
|
|
124
|
+
return this.buildQuery(collectionPath, options).query;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Build query with sorting
|
|
129
|
+
*/
|
|
130
|
+
buildSortedQuery(
|
|
131
|
+
collectionPath: string,
|
|
132
|
+
sortField: string,
|
|
133
|
+
direction: 'asc' | 'desc' = 'asc'
|
|
134
|
+
): Query {
|
|
135
|
+
const options = createQueryOptions({
|
|
136
|
+
sort: [{ field: sortField, direction }],
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return this.buildQuery(collectionPath, options).query;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Build query with limit
|
|
144
|
+
*/
|
|
145
|
+
buildLimitedQuery(collectionPath: string, limitValue: number): Query {
|
|
146
|
+
const options = createQueryOptions({
|
|
147
|
+
pagination: { limit: limitValue },
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
return this.buildQuery(collectionPath, options).query;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
}
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Build query with date range
|
|
5
|
+
*/
|
|
6
|
+
buildDateRangeQuery(
|
|
7
|
+
collectionPath: string,
|
|
8
|
+
field: string,
|
|
9
|
+
startDate?: Date,
|
|
10
|
+
endDate?: Date
|
|
11
|
+
): Query {
|
|
12
|
+
const options = createQueryOptions({
|
|
13
|
+
dateRange: { field, startDate, endDate },
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
return this.buildQuery(collectionPath, options).query;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Validate query options
|
|
21
|
+
*/
|
|
22
|
+
validateOptions(options: QueryOptions): { valid: boolean; errors: string[] } {
|
|
23
|
+
return options.validate();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check if query is empty (no filters)
|
|
28
|
+
*/
|
|
29
|
+
isEmptyQuery(options: QueryOptions): boolean {
|
|
30
|
+
return options.isEmpty();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -2,35 +2,24 @@
|
|
|
2
2
|
* Query Options Value Object (Main)
|
|
3
3
|
* Single Responsibility: Encapsulate query configuration options
|
|
4
4
|
*
|
|
5
|
-
* Value object that represents query options in a type-safe way.
|
|
6
|
-
* Provides validation and business logic for query configuration.
|
|
7
|
-
*
|
|
8
5
|
* Max lines: 150 (enforced for maintainability)
|
|
9
6
|
*/
|
|
10
7
|
|
|
11
8
|
import type { WhereFilterOp, OrderByDirection } from 'firebase/firestore';
|
|
12
9
|
import { WhereClause } from './WhereClause';
|
|
10
|
+
import * as Factory from './QueryOptionsFactory';
|
|
13
11
|
|
|
14
|
-
/**
|
|
15
|
-
* Sort options
|
|
16
|
-
*/
|
|
17
12
|
export interface SortOptions {
|
|
18
13
|
readonly field: string;
|
|
19
14
|
readonly direction: OrderByDirection;
|
|
20
15
|
}
|
|
21
16
|
|
|
22
|
-
/**
|
|
23
|
-
* Date range options
|
|
24
|
-
*/
|
|
25
17
|
export interface DateRangeOptions {
|
|
26
18
|
readonly field: string;
|
|
27
19
|
readonly startDate?: Date;
|
|
28
20
|
readonly endDate?: Date;
|
|
29
21
|
}
|
|
30
22
|
|
|
31
|
-
/**
|
|
32
|
-
* Pagination options
|
|
33
|
-
*/
|
|
34
23
|
export interface PaginationOptions {
|
|
35
24
|
readonly cursor?: number;
|
|
36
25
|
readonly limit?: number;
|
|
@@ -38,10 +27,6 @@ export interface PaginationOptions {
|
|
|
38
27
|
readonly startAt?: number;
|
|
39
28
|
}
|
|
40
29
|
|
|
41
|
-
/**
|
|
42
|
-
* Query options value object
|
|
43
|
-
* Immutable configuration for Firestore queries
|
|
44
|
-
*/
|
|
45
30
|
export class QueryOptions {
|
|
46
31
|
readonly whereClauses: readonly WhereClause[];
|
|
47
32
|
readonly sortOptions: readonly SortOptions[];
|
|
@@ -60,16 +45,10 @@ export class QueryOptions {
|
|
|
60
45
|
this.pagination = pagination ? Object.freeze(pagination) : null;
|
|
61
46
|
}
|
|
62
47
|
|
|
63
|
-
/**
|
|
64
|
-
* Create empty query options
|
|
65
|
-
*/
|
|
66
48
|
static empty(): QueryOptions {
|
|
67
49
|
return new QueryOptions([], [], null, null);
|
|
68
50
|
}
|
|
69
51
|
|
|
70
|
-
/**
|
|
71
|
-
* Create query options from partial configuration
|
|
72
|
-
*/
|
|
73
52
|
static create(options: {
|
|
74
53
|
where?: WhereClause[];
|
|
75
54
|
sort?: SortOptions[];
|
|
@@ -84,103 +63,76 @@ export class QueryOptions {
|
|
|
84
63
|
);
|
|
85
64
|
}
|
|
86
65
|
|
|
87
|
-
/**
|
|
88
|
-
* Add where clause
|
|
89
|
-
*/
|
|
90
66
|
withWhere(clause: WhereClause): QueryOptions {
|
|
91
67
|
return new QueryOptions(
|
|
92
68
|
[...this.whereClauses, clause] as WhereClause[],
|
|
93
|
-
this.sortOptions,
|
|
69
|
+
[...this.sortOptions] as SortOptions[],
|
|
94
70
|
this.dateRange,
|
|
95
71
|
this.pagination
|
|
96
72
|
);
|
|
97
73
|
}
|
|
98
74
|
|
|
99
|
-
/**
|
|
100
|
-
* Add sort option
|
|
101
|
-
*/
|
|
102
75
|
withSort(sort: SortOptions): QueryOptions {
|
|
103
76
|
return new QueryOptions(
|
|
104
|
-
this.whereClauses,
|
|
77
|
+
[...this.whereClauses] as WhereClause[],
|
|
105
78
|
[...this.sortOptions, sort] as SortOptions[],
|
|
106
79
|
this.dateRange,
|
|
107
80
|
this.pagination
|
|
108
81
|
);
|
|
109
82
|
}
|
|
110
83
|
|
|
111
|
-
/**
|
|
112
|
-
* Set date range
|
|
113
|
-
*/
|
|
114
84
|
withDateRange(dateRange: DateRangeOptions): QueryOptions {
|
|
115
85
|
return new QueryOptions(
|
|
116
|
-
this.whereClauses,
|
|
117
|
-
this.sortOptions,
|
|
86
|
+
[...this.whereClauses] as WhereClause[],
|
|
87
|
+
[...this.sortOptions] as SortOptions[],
|
|
118
88
|
dateRange,
|
|
119
89
|
this.pagination
|
|
120
90
|
);
|
|
121
91
|
}
|
|
122
92
|
|
|
123
|
-
/**
|
|
124
|
-
* Set pagination
|
|
125
|
-
*/
|
|
126
93
|
withPagination(pagination: PaginationOptions): QueryOptions {
|
|
127
94
|
return new QueryOptions(
|
|
128
|
-
this.whereClauses,
|
|
129
|
-
this.sortOptions,
|
|
95
|
+
[...this.whereClauses] as WhereClause[],
|
|
96
|
+
[...this.sortOptions] as SortOptions[],
|
|
130
97
|
this.dateRange,
|
|
131
98
|
pagination
|
|
132
99
|
);
|
|
133
100
|
}
|
|
134
101
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
102
|
+
withLimit(limit: number): QueryOptions {
|
|
103
|
+
return this.withPagination({
|
|
104
|
+
...this.pagination,
|
|
105
|
+
limit,
|
|
106
|
+
} as PaginationOptions);
|
|
107
|
+
}
|
|
108
|
+
|
|
138
109
|
clearWhere(): QueryOptions {
|
|
139
|
-
return new QueryOptions([], this.sortOptions, this.dateRange, this.pagination);
|
|
110
|
+
return new QueryOptions([], [...this.sortOptions] as SortOptions[], this.dateRange, this.pagination);
|
|
140
111
|
}
|
|
141
112
|
|
|
142
|
-
/**
|
|
143
|
-
* Remove all sort options
|
|
144
|
-
*/
|
|
145
113
|
clearSort(): QueryOptions {
|
|
146
|
-
return new QueryOptions(this.whereClauses, [], this.dateRange, this.pagination);
|
|
114
|
+
return new QueryOptions([...this.whereClauses] as WhereClause[], [], this.dateRange, this.pagination);
|
|
147
115
|
}
|
|
148
116
|
|
|
149
|
-
/**
|
|
150
|
-
* Remove date range
|
|
151
|
-
*/
|
|
152
117
|
clearDateRange(): QueryOptions {
|
|
153
|
-
return new QueryOptions(this.whereClauses, this.sortOptions, null, this.pagination);
|
|
118
|
+
return new QueryOptions([...this.whereClauses] as WhereClause[], [...this.sortOptions] as SortOptions[], null, this.pagination);
|
|
154
119
|
}
|
|
155
120
|
|
|
156
|
-
/**
|
|
157
|
-
* Remove pagination
|
|
158
|
-
*/
|
|
159
121
|
clearPagination(): QueryOptions {
|
|
160
|
-
return new QueryOptions(this.whereClauses, this.sortOptions, this.dateRange, null);
|
|
122
|
+
return new QueryOptions([...this.whereClauses] as WhereClause[], [...this.sortOptions] as SortOptions[], this.dateRange, null);
|
|
161
123
|
}
|
|
162
124
|
|
|
163
|
-
/**
|
|
164
|
-
* Clone with modifications
|
|
165
|
-
*/
|
|
166
125
|
clone(modifications: {
|
|
167
126
|
where?: WhereClause[];
|
|
168
127
|
sort?: SortOptions[];
|
|
169
128
|
dateRange?: DateRangeOptions | null;
|
|
170
129
|
pagination?: PaginationOptions | null;
|
|
171
130
|
}): QueryOptions {
|
|
172
|
-
return
|
|
173
|
-
where: modifications.where ?? [...this.whereClauses] as WhereClause[],
|
|
174
|
-
sort: modifications.sort ?? [...this.sortOptions] as SortOptions[],
|
|
175
|
-
dateRange: modifications.dateRange ?? this.dateRange ?? null,
|
|
176
|
-
pagination: modifications.pagination ?? this.pagination ?? null,
|
|
177
|
-
});
|
|
131
|
+
return Factory.clone(this, modifications);
|
|
178
132
|
}
|
|
179
133
|
}
|
|
180
134
|
|
|
181
|
-
|
|
182
|
-
* Factory function to create query options
|
|
183
|
-
*/
|
|
135
|
+
// Factory function
|
|
184
136
|
export function createQueryOptions(options?: {
|
|
185
137
|
where?: WhereClause[];
|
|
186
138
|
sort?: SortOptions[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Query Options Value Object
|
|
2
|
+
* Query Options Value Object (Main)
|
|
3
3
|
* Single Responsibility: Encapsulate query configuration options
|
|
4
4
|
*
|
|
5
5
|
* Value object that represents query options in a type-safe way.
|
|
@@ -89,7 +89,7 @@ export class QueryOptions {
|
|
|
89
89
|
*/
|
|
90
90
|
withWhere(clause: WhereClause): QueryOptions {
|
|
91
91
|
return new QueryOptions(
|
|
92
|
-
[...this.whereClauses, clause] as WhereClause[]
|
|
92
|
+
[...this.whereClauses, clause] as WhereClause[],
|
|
93
93
|
this.sortOptions,
|
|
94
94
|
this.dateRange,
|
|
95
95
|
this.pagination
|
|
@@ -160,135 +160,6 @@ export class QueryOptions {
|
|
|
160
160
|
return new QueryOptions(this.whereClauses, this.sortOptions, this.dateRange, null);
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
/**
|
|
164
|
-
* Check if has any where clauses
|
|
165
|
-
*/
|
|
166
|
-
hasWhereClauses(): boolean {
|
|
167
|
-
return this.whereClauses.length > 0;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Check if has any sort options
|
|
172
|
-
*/
|
|
173
|
-
hasSort(): boolean {
|
|
174
|
-
return this.sortOptions.length > 0;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Check if has date range
|
|
179
|
-
*/
|
|
180
|
-
hasDateRange(): boolean {
|
|
181
|
-
return this.dateRange !== null;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Check if has pagination
|
|
186
|
-
*/
|
|
187
|
-
hasPagination(): boolean {
|
|
188
|
-
return this.pagination !== null;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Check if is empty (no options set)
|
|
193
|
-
*/
|
|
194
|
-
isEmpty(): boolean {
|
|
195
|
-
return !this.hasWhereClauses() &&
|
|
196
|
-
!this.hasSort() &&
|
|
197
|
-
!this.hasDateRange() &&
|
|
198
|
-
!this.hasPagination();
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Validate query options
|
|
203
|
-
*/
|
|
204
|
-
validate(): { valid: boolean; errors: string[] } {
|
|
205
|
-
const errors: string[] = [];
|
|
206
|
-
|
|
207
|
-
// Validate where clauses
|
|
208
|
-
for (const clause of this.whereClauses) {
|
|
209
|
-
const clauseValidation = clause.validate();
|
|
210
|
-
if (!clauseValidation.valid) {
|
|
211
|
-
errors.push(...clauseValidation.errors);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Validate sort options
|
|
216
|
-
for (const sort of this.sortOptions) {
|
|
217
|
-
if (!sort.field || typeof sort.field !== 'string') {
|
|
218
|
-
errors.push('Sort field must be a non-empty string');
|
|
219
|
-
}
|
|
220
|
-
if (sort.direction !== 'asc' && sort.direction !== 'desc') {
|
|
221
|
-
errors.push('Sort direction must be "asc" or "desc"');
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Validate date range
|
|
226
|
-
if (this.dateRange) {
|
|
227
|
-
if (!this.dateRange.field || typeof this.dateRange.field !== 'string') {
|
|
228
|
-
errors.push('Date range field must be a non-empty string');
|
|
229
|
-
}
|
|
230
|
-
if (this.dateRange.startDate && !(this.dateRange.startDate instanceof Date)) {
|
|
231
|
-
errors.push('Start date must be a Date instance');
|
|
232
|
-
}
|
|
233
|
-
if (this.dateRange.endDate && !(this.dateRange.endDate instanceof Date)) {
|
|
234
|
-
errors.push('End date must be a Date instance');
|
|
235
|
-
}
|
|
236
|
-
if (
|
|
237
|
-
this.dateRange.startDate &&
|
|
238
|
-
this.dateRange.endDate &&
|
|
239
|
-
this.dateRange.startDate > this.dateRange.endDate
|
|
240
|
-
) {
|
|
241
|
-
errors.push('Start date must be before end date');
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Validate pagination
|
|
246
|
-
if (this.pagination) {
|
|
247
|
-
if (this.pagination.limit !== undefined && (typeof this.pagination.limit !== 'number' || this.pagination.limit <= 0)) {
|
|
248
|
-
errors.push('Pagination limit must be a positive number');
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return {
|
|
253
|
-
valid: errors.length === 0,
|
|
254
|
-
errors,
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Convert to plain object (for serialization)
|
|
260
|
-
*/
|
|
261
|
-
toObject(): {
|
|
262
|
-
where: WhereClause[];
|
|
263
|
-
sort: SortOptions[];
|
|
264
|
-
dateRange: DateRangeOptions | null;
|
|
265
|
-
pagination: PaginationOptions | null;
|
|
266
|
-
} {
|
|
267
|
-
return {
|
|
268
|
-
where: [...this.whereClauses] as WhereClause[],
|
|
269
|
-
sort: [...this.sortOptions] as SortOptions[],
|
|
270
|
-
dateRange: this.dateRange,
|
|
271
|
-
pagination: this.pagination,
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Create from plain object
|
|
277
|
-
*/
|
|
278
|
-
static fromObject(obj: {
|
|
279
|
-
where?: Array<{ field: string; operator: WhereFilterOp; value: unknown }>;
|
|
280
|
-
sort?: SortOptions[];
|
|
281
|
-
dateRange?: DateRangeOptions;
|
|
282
|
-
pagination?: PaginationOptions;
|
|
283
|
-
}): QueryOptions {
|
|
284
|
-
return QueryOptions.create({
|
|
285
|
-
where: obj.where?.map(w => WhereClause.create(w.field, w.operator, w.value)) || [],
|
|
286
|
-
sort: obj.sort || [],
|
|
287
|
-
dateRange: obj.dateRange || null,
|
|
288
|
-
pagination: obj.pagination || null,
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
|
|
292
163
|
/**
|
|
293
164
|
* Clone with modifications
|
|
294
165
|
*/
|
|
@@ -299,10 +170,10 @@ export class QueryOptions {
|
|
|
299
170
|
pagination?: PaginationOptions | null;
|
|
300
171
|
}): QueryOptions {
|
|
301
172
|
return QueryOptions.create({
|
|
302
|
-
where: modifications.where ?? this.whereClauses,
|
|
303
|
-
sort: modifications.sort ?? this.sortOptions,
|
|
304
|
-
dateRange: modifications.dateRange ?? this.dateRange,
|
|
305
|
-
pagination: modifications.pagination ?? this.pagination,
|
|
173
|
+
where: modifications.where ?? [...this.whereClauses] as WhereClause[],
|
|
174
|
+
sort: modifications.sort ?? [...this.sortOptions] as SortOptions[],
|
|
175
|
+
dateRange: modifications.dateRange ?? this.dateRange ?? null,
|
|
176
|
+
pagination: modifications.pagination ?? this.pagination ?? null,
|
|
306
177
|
});
|
|
307
178
|
}
|
|
308
179
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query Options Factory
|
|
3
|
+
* Single Responsibility: Create query options
|
|
4
|
+
*
|
|
5
|
+
* Max lines: 150 (enforced for maintainability)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { WhereFilterOp, OrderByDirection } from 'firebase/firestore';
|
|
9
|
+
import type { SortOptions, DateRangeOptions, PaginationOptions } from './QueryOptions';
|
|
10
|
+
import { WhereClause } from './WhereClause';
|
|
11
|
+
import { QueryOptions } from './QueryOptions';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Create empty query options
|
|
15
|
+
*/
|
|
16
|
+
export function empty(): QueryOptions {
|
|
17
|
+
return QueryOptions.empty();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Create query options from partial configuration
|
|
22
|
+
*/
|
|
23
|
+
export function create(options: {
|
|
24
|
+
where?: WhereClause[];
|
|
25
|
+
sort?: SortOptions[];
|
|
26
|
+
dateRange?: DateRangeOptions;
|
|
27
|
+
pagination?: PaginationOptions;
|
|
28
|
+
}): QueryOptions {
|
|
29
|
+
return QueryOptions.create(options);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Create query options with where clause
|
|
34
|
+
*/
|
|
35
|
+
export function withWhere(
|
|
36
|
+
baseOptions: QueryOptions,
|
|
37
|
+
field: string,
|
|
38
|
+
operator: WhereFilterOp,
|
|
39
|
+
value: unknown
|
|
40
|
+
): QueryOptions {
|
|
41
|
+
const clause = new WhereClause(field, operator, value);
|
|
42
|
+
return baseOptions.withWhere(clause);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Create query options with sort
|
|
47
|
+
*/
|
|
48
|
+
export function withSort(
|
|
49
|
+
baseOptions: QueryOptions,
|
|
50
|
+
field: string,
|
|
51
|
+
direction: OrderByDirection
|
|
52
|
+
): QueryOptions {
|
|
53
|
+
return baseOptions.withSort({ field, direction });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Create query options with date range
|
|
58
|
+
*/
|
|
59
|
+
export function withDateRange(
|
|
60
|
+
baseOptions: QueryOptions,
|
|
61
|
+
field: string,
|
|
62
|
+
startDate?: Date,
|
|
63
|
+
endDate?: Date
|
|
64
|
+
): QueryOptions {
|
|
65
|
+
return baseOptions.withDateRange({ field, startDate, endDate });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Create query options with pagination
|
|
70
|
+
*/
|
|
71
|
+
export function withPagination(
|
|
72
|
+
baseOptions: QueryOptions,
|
|
73
|
+
pagination: PaginationOptions
|
|
74
|
+
): QueryOptions {
|
|
75
|
+
return baseOptions.withPagination(pagination);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Create query options with limit
|
|
80
|
+
*/
|
|
81
|
+
export function withLimit(baseOptions: QueryOptions, limit: number): QueryOptions {
|
|
82
|
+
return baseOptions.withLimit(limit);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Clone query options with modifications
|
|
87
|
+
*/
|
|
88
|
+
export function clone(baseOptions: QueryOptions, modifications: {
|
|
89
|
+
where?: WhereClause[];
|
|
90
|
+
sort?: SortOptions[];
|
|
91
|
+
dateRange?: DateRangeOptions | null;
|
|
92
|
+
pagination?: PaginationOptions | null;
|
|
93
|
+
}): QueryOptions {
|
|
94
|
+
return baseOptions.clone(modifications);
|
|
95
|
+
}
|