@sneat/auth-core 0.1.3 → 0.1.4
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/esm2022/index.js +8 -0
- package/esm2022/index.js.map +1 -0
- package/esm2022/lib/login-required-service.service.js +20 -0
- package/esm2022/lib/login-required-service.service.js.map +1 -0
- package/esm2022/lib/private-token-store.service.js +36 -0
- package/esm2022/lib/private-token-store.service.js.map +1 -0
- package/esm2022/lib/sneat-auth-guard.js +79 -0
- package/esm2022/lib/sneat-auth-guard.js.map +1 -0
- package/esm2022/lib/sneat-auth-state-service.js +267 -0
- package/esm2022/lib/sneat-auth-state-service.js.map +1 -0
- package/{src/lib/sneat-auth.interface.ts → esm2022/lib/sneat-auth.interface.js} +1 -5
- package/esm2022/lib/sneat-auth.interface.js.map +1 -0
- package/esm2022/lib/telegram-auth.service.js +39 -0
- package/esm2022/lib/telegram-auth.service.js.map +1 -0
- package/esm2022/lib/user/index.js +3 -0
- package/esm2022/lib/user/index.js.map +1 -0
- package/esm2022/lib/user/sneat-user.service.js +171 -0
- package/esm2022/lib/user/sneat-user.service.js.map +1 -0
- package/esm2022/lib/user/user-record.service.js +30 -0
- package/esm2022/lib/user/user-record.service.js.map +1 -0
- package/esm2022/sneat-auth-core.js +5 -0
- package/esm2022/sneat-auth-core.js.map +1 -0
- package/lib/login-required-service.service.d.ts +6 -0
- package/lib/private-token-store.service.d.ts +8 -0
- package/lib/sneat-auth-guard.d.ts +26 -0
- package/lib/sneat-auth-state-service.d.ts +51 -0
- package/lib/sneat-auth.interface.d.ts +5 -0
- package/lib/telegram-auth.service.d.ts +9 -0
- package/lib/user/sneat-user.service.d.ts +34 -0
- package/lib/user/user-record.service.d.ts +25 -0
- package/package.json +14 -2
- package/sneat-auth-core.d.ts +5 -0
- package/eslint.config.js +0 -7
- package/ng-package.json +0 -7
- package/project.json +0 -38
- package/src/lib/login-required-service.service.spec.ts +0 -39
- package/src/lib/login-required-service.service.ts +0 -14
- package/src/lib/private-token-store.service.spec.ts +0 -75
- package/src/lib/private-token-store.service.ts +0 -36
- package/src/lib/sneat-auth-guard.spec.ts +0 -124
- package/src/lib/sneat-auth-guard.ts +0 -107
- package/src/lib/sneat-auth-state-service.spec.ts +0 -332
- package/src/lib/sneat-auth-state-service.ts +0 -387
- package/src/lib/sneat-auth.interface.spec.ts +0 -39
- package/src/lib/telegram-auth.service.spec.ts +0 -186
- package/src/lib/telegram-auth.service.ts +0 -49
- package/src/lib/user/sneat-user.service.spec.ts +0 -151
- package/src/lib/user/sneat-user.service.ts +0 -266
- package/src/lib/user/user-record.service.spec.ts +0 -145
- package/src/lib/user/user-record.service.ts +0 -38
- package/src/test-setup.ts +0 -3
- package/tsconfig.json +0 -13
- package/tsconfig.lib.json +0 -19
- package/tsconfig.lib.prod.json +0 -7
- package/tsconfig.spec.json +0 -31
- package/vite.config.mts +0 -10
- /package/{src/index.ts → index.d.ts} +0 -0
- /package/{src/lib/user/index.ts → lib/user/index.d.ts} +0 -0
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { TestBed } from '@angular/core/testing';
|
|
2
|
-
import {
|
|
3
|
-
Firestore as AngularFirestore,
|
|
4
|
-
CollectionReference,
|
|
5
|
-
} from '@angular/fire/firestore';
|
|
6
|
-
import { SneatApiService } from '@sneat/api';
|
|
7
|
-
import { ErrorLogger } from '@sneat/core';
|
|
8
|
-
import {
|
|
9
|
-
SneatAuthStateService,
|
|
10
|
-
ISneatAuthState,
|
|
11
|
-
} from '../sneat-auth-state-service';
|
|
12
|
-
import { UserRecordService } from './user-record.service';
|
|
13
|
-
import { SneatUserService } from './sneat-user.service';
|
|
14
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
15
|
-
import { of, Subject, firstValueFrom } from 'rxjs';
|
|
16
|
-
|
|
17
|
-
// Mock firestore functions
|
|
18
|
-
vi.mock('@angular/fire/firestore', async () => {
|
|
19
|
-
const actual = await vi.importActual('@angular/fire/firestore');
|
|
20
|
-
return {
|
|
21
|
-
...actual,
|
|
22
|
-
collection: vi
|
|
23
|
-
.fn()
|
|
24
|
-
.mockReturnValue({ id: 'users' } as unknown as CollectionReference),
|
|
25
|
-
doc: vi.fn(),
|
|
26
|
-
onSnapshot: vi.fn(),
|
|
27
|
-
};
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
describe('SneatUserService', () => {
|
|
31
|
-
let service: SneatUserService;
|
|
32
|
-
let authStateSubject: Subject<ISneatAuthState>;
|
|
33
|
-
let sneatApiServiceMock: { post: ReturnType<typeof vi.fn> };
|
|
34
|
-
let userRecordServiceMock: { initUserRecord: ReturnType<typeof vi.fn> };
|
|
35
|
-
let firestoreMock: Record<string, unknown>;
|
|
36
|
-
|
|
37
|
-
beforeEach(() => {
|
|
38
|
-
authStateSubject = new Subject<ISneatAuthState>();
|
|
39
|
-
|
|
40
|
-
sneatApiServiceMock = {
|
|
41
|
-
post: vi.fn().mockReturnValue(of(void 0)),
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
userRecordServiceMock = {
|
|
45
|
-
initUserRecord: vi
|
|
46
|
-
.fn()
|
|
47
|
-
.mockReturnValue(of({ id: 'user123', title: 'Test User' })),
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
firestoreMock = {
|
|
51
|
-
app: {},
|
|
52
|
-
type: 'firestore',
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
TestBed.configureTestingModule({
|
|
56
|
-
providers: [
|
|
57
|
-
SneatUserService,
|
|
58
|
-
{
|
|
59
|
-
provide: AngularFirestore,
|
|
60
|
-
useValue: firestoreMock,
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
provide: ErrorLogger,
|
|
64
|
-
useValue: {
|
|
65
|
-
logError: vi.fn(),
|
|
66
|
-
logErrorHandler: vi.fn().mockReturnValue(() => undefined),
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
provide: SneatAuthStateService,
|
|
71
|
-
useValue: {
|
|
72
|
-
authState: authStateSubject.asObservable(),
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
provide: SneatApiService,
|
|
77
|
-
useValue: sneatApiServiceMock,
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
provide: UserRecordService,
|
|
81
|
-
useValue: userRecordServiceMock,
|
|
82
|
-
},
|
|
83
|
-
],
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
service = TestBed.inject(SneatUserService);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('should be created', () => {
|
|
90
|
-
expect(service).toBeTruthy();
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('should have currentUserID as undefined initially', () => {
|
|
94
|
-
expect(service.currentUserID).toBeUndefined();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should call setUserCountry with correct parameters', async () => {
|
|
98
|
-
const countryID = 'US';
|
|
99
|
-
await firstValueFrom(service.setUserCountry(countryID));
|
|
100
|
-
expect(sneatApiServiceMock.post).toHaveBeenCalledWith(
|
|
101
|
-
'users/set_user_country',
|
|
102
|
-
{ countryID },
|
|
103
|
-
);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('should update currentUserID when user signs in', () => {
|
|
107
|
-
const authState: ISneatAuthState = {
|
|
108
|
-
status: 'authenticated',
|
|
109
|
-
user: {
|
|
110
|
-
uid: 'test-uid-123',
|
|
111
|
-
email: 'test@example.com',
|
|
112
|
-
emailVerified: true,
|
|
113
|
-
displayName: 'Test User',
|
|
114
|
-
providerId: 'google.com',
|
|
115
|
-
isAnonymous: false,
|
|
116
|
-
providerData: [],
|
|
117
|
-
photoURL: null,
|
|
118
|
-
phoneNumber: null,
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
service.onUserSignedIn(authState);
|
|
123
|
-
|
|
124
|
-
expect(service.currentUserID).toBe('test-uid-123');
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('should not change currentUserID if uid is the same', () => {
|
|
128
|
-
const authState: ISneatAuthState = {
|
|
129
|
-
status: 'authenticated',
|
|
130
|
-
user: {
|
|
131
|
-
uid: 'test-uid-123',
|
|
132
|
-
email: 'test@example.com',
|
|
133
|
-
emailVerified: true,
|
|
134
|
-
displayName: 'Test User',
|
|
135
|
-
providerId: 'google.com',
|
|
136
|
-
isAnonymous: false,
|
|
137
|
-
providerData: [],
|
|
138
|
-
photoURL: null,
|
|
139
|
-
phoneNumber: null,
|
|
140
|
-
},
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
service.onUserSignedIn(authState);
|
|
144
|
-
const firstUserID = service.currentUserID;
|
|
145
|
-
|
|
146
|
-
service.onUserSignedIn(authState); // Second call with same uid
|
|
147
|
-
|
|
148
|
-
expect(service.currentUserID).toBe(firstUserID);
|
|
149
|
-
expect(service.currentUserID).toBe('test-uid-123');
|
|
150
|
-
});
|
|
151
|
-
});
|
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Injectable,
|
|
3
|
-
inject,
|
|
4
|
-
Injector,
|
|
5
|
-
runInInjectionContext,
|
|
6
|
-
} from '@angular/core';
|
|
7
|
-
import {
|
|
8
|
-
// Action,
|
|
9
|
-
Firestore as AngularFirestore,
|
|
10
|
-
CollectionReference,
|
|
11
|
-
DocumentSnapshot,
|
|
12
|
-
collection,
|
|
13
|
-
doc,
|
|
14
|
-
onSnapshot,
|
|
15
|
-
Unsubscribe,
|
|
16
|
-
} from '@angular/fire/firestore';
|
|
17
|
-
import { SneatApiService } from '@sneat/api';
|
|
18
|
-
import { IUserRecord } from '@sneat/auth-models';
|
|
19
|
-
import { ErrorLogger, IErrorLogger } from '@sneat/core';
|
|
20
|
-
import {
|
|
21
|
-
initialSneatAuthState,
|
|
22
|
-
ISneatAuthState,
|
|
23
|
-
ISneatAuthUser,
|
|
24
|
-
SneatAuthStateService,
|
|
25
|
-
} from '../sneat-auth-state-service';
|
|
26
|
-
import { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';
|
|
27
|
-
import {
|
|
28
|
-
IInitUserRecordRequest,
|
|
29
|
-
UserRecordService,
|
|
30
|
-
} from './user-record.service';
|
|
31
|
-
|
|
32
|
-
export interface ISneatUserState extends ISneatAuthState {
|
|
33
|
-
record?: IUserRecord | null; // undefined => not loaded yet, null = does not exists
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const UsersCollection = 'users';
|
|
37
|
-
|
|
38
|
-
@Injectable({ providedIn: 'root' }) // TODO: lazy loading
|
|
39
|
-
export class SneatUserService {
|
|
40
|
-
private readonly errorLogger = inject<IErrorLogger>(ErrorLogger);
|
|
41
|
-
private readonly sneatApiService = inject(SneatApiService);
|
|
42
|
-
private readonly userRecordService = inject(UserRecordService);
|
|
43
|
-
|
|
44
|
-
// private userDocSubscription?: Subscription;
|
|
45
|
-
private readonly injector = inject(Injector);
|
|
46
|
-
private readonly userCollection: CollectionReference<IUserRecord>;
|
|
47
|
-
private readonly userDocRef = (uid: string) =>
|
|
48
|
-
runInInjectionContext(this.injector, () => doc(this.userCollection, uid));
|
|
49
|
-
|
|
50
|
-
private uid?: string;
|
|
51
|
-
private $userTitle?: string;
|
|
52
|
-
|
|
53
|
-
private readonly userChanged$ = new ReplaySubject<string | undefined>(1);
|
|
54
|
-
public readonly userChanged = this.userChanged$.asObservable();
|
|
55
|
-
|
|
56
|
-
private readonly userState$ = new BehaviorSubject<ISneatUserState>(
|
|
57
|
-
initialSneatAuthState,
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
public readonly userState = this.userState$.asObservable();
|
|
61
|
-
|
|
62
|
-
private _unsubscribeFromUserDoc?: Unsubscribe;
|
|
63
|
-
|
|
64
|
-
private unsubscribeFromUserDoc(_from: string) {
|
|
65
|
-
if (this._unsubscribeFromUserDoc) {
|
|
66
|
-
// console.log(
|
|
67
|
-
// 'SneatUserService.unsubscribeFromUserDoc() called from ' + _from,
|
|
68
|
-
// );
|
|
69
|
-
this._unsubscribeFromUserDoc();
|
|
70
|
-
this._unsubscribeFromUserDoc = undefined;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
constructor() {
|
|
75
|
-
const afs = inject(AngularFirestore);
|
|
76
|
-
const sneatAuthStateService = inject(SneatAuthStateService);
|
|
77
|
-
this.userCollection = collection(
|
|
78
|
-
afs,
|
|
79
|
-
UsersCollection,
|
|
80
|
-
) as CollectionReference<IUserRecord>;
|
|
81
|
-
sneatAuthStateService.authState.subscribe({
|
|
82
|
-
next: this.onAuthStateChanged,
|
|
83
|
-
error: this.errorLogger.logErrorHandler('failed to get sneat auth state'),
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
public get currentUserID(): string | undefined {
|
|
88
|
-
return this.uid;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// public get userTitle(): string | undefined {
|
|
92
|
-
// return this.$userTitle;
|
|
93
|
-
// }
|
|
94
|
-
|
|
95
|
-
public setUserCountry(countryID: string): Observable<void> {
|
|
96
|
-
return this.sneatApiService.post('users/set_user_country', { countryID });
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
public onUserSignedIn(authState: ISneatAuthState): void {
|
|
100
|
-
const authUser = authState.user;
|
|
101
|
-
// afUser.getIdToken().then(idToken => {
|
|
102
|
-
// console.log('Firebase idToken:', idToken);
|
|
103
|
-
// }).catch(err => this.errorLoggerService.logError(err, 'Failed to get Firebase ID token'));
|
|
104
|
-
if (authUser?.email && authUser.emailVerified) {
|
|
105
|
-
this.$userTitle = authUser.email;
|
|
106
|
-
}
|
|
107
|
-
if (this.uid === authUser?.uid) {
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
this.unsubscribeFromUserDoc('onUserSignedIn()');
|
|
111
|
-
if (!authUser) {
|
|
112
|
-
if (this.userState$.value?.record !== null) {
|
|
113
|
-
this.userState$.next({ ...this.userState$.value });
|
|
114
|
-
}
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
const { uid } = authUser;
|
|
118
|
-
this.uid = uid;
|
|
119
|
-
this.userState$.next({
|
|
120
|
-
...authState,
|
|
121
|
-
});
|
|
122
|
-
this.userChanged$.next(uid);
|
|
123
|
-
this.watchUserRecord(uid, authState);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
private watchUserRecord(uid: string, authState: ISneatAuthState): void {
|
|
127
|
-
// console.log(
|
|
128
|
-
// `SneatUserService.watchUserRecord(uid=${uid}): Loading user record...`,
|
|
129
|
-
// );
|
|
130
|
-
this.unsubscribeFromUserDoc('whatUserRecord()');
|
|
131
|
-
|
|
132
|
-
// TODO: Remove - setTimeout() not needed but trying to troubleshoot user record issue
|
|
133
|
-
setTimeout(() => {
|
|
134
|
-
try {
|
|
135
|
-
const userDocRef = this.userDocRef(uid);
|
|
136
|
-
this._unsubscribeFromUserDoc = runInInjectionContext(
|
|
137
|
-
this.injector,
|
|
138
|
-
() =>
|
|
139
|
-
onSnapshot(userDocRef, {
|
|
140
|
-
next: (userDocSnapshot) => {
|
|
141
|
-
// console.log(
|
|
142
|
-
// `SneatUserService.watchUserRecord(uid=${uid}) => userDocSnapshot:`,
|
|
143
|
-
// userDocSnapshot,
|
|
144
|
-
// );
|
|
145
|
-
this.onAuthStateChanged(authState);
|
|
146
|
-
this.userDocChanged(userDocSnapshot, authState);
|
|
147
|
-
},
|
|
148
|
-
error: (err) => {
|
|
149
|
-
console.error(
|
|
150
|
-
`SneatUserService.watchUserRecord(uid=${uid}) => failed:`,
|
|
151
|
-
err,
|
|
152
|
-
);
|
|
153
|
-
},
|
|
154
|
-
}),
|
|
155
|
-
);
|
|
156
|
-
} catch (err) {
|
|
157
|
-
console.error(
|
|
158
|
-
`SneatUserService.watchUserRecord(uid=${uid}) => Failed to setup watcher for user record::`,
|
|
159
|
-
err,
|
|
160
|
-
);
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
}, 100);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
private onAuthStateChanged = (authState: ISneatAuthState): void => {
|
|
167
|
-
// console.log('SneatUserService => authState changed:', authState);
|
|
168
|
-
if (authState.user) {
|
|
169
|
-
this.onUserSignedIn(authState);
|
|
170
|
-
} else {
|
|
171
|
-
this.userState$.next(authState);
|
|
172
|
-
this.userChanged$.next(undefined);
|
|
173
|
-
this.onUserSignedOut();
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
private userDocChanged(
|
|
178
|
-
userDocSnapshot: DocumentSnapshot<IUserRecord>,
|
|
179
|
-
authState: ISneatAuthState,
|
|
180
|
-
): void {
|
|
181
|
-
// console.log(
|
|
182
|
-
// 'SneatUserService.userDocChanged() => userDocSnapshot.exists:',
|
|
183
|
-
// userDocSnapshot.exists(),
|
|
184
|
-
// 'authState:',
|
|
185
|
-
// authState,
|
|
186
|
-
// 'userDocSnapshot:',
|
|
187
|
-
// userDocSnapshot,
|
|
188
|
-
// );
|
|
189
|
-
if (userDocSnapshot.ref.id !== this.uid) {
|
|
190
|
-
console.error(
|
|
191
|
-
'userDocSnapshot.ref.id !== this.uid - Should always be equal as we unsubscribe if uid changes',
|
|
192
|
-
);
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
// console.log('SneatUserService => userDocSnapshot.exists:', userDocSnapshot.exists)
|
|
196
|
-
const authUser = authState.user;
|
|
197
|
-
if (authUser && !userDocSnapshot.exists()) {
|
|
198
|
-
this.initUserRecordFromAuthUser(authUser);
|
|
199
|
-
}
|
|
200
|
-
const userRecord: IUserRecord | null = userDocSnapshot.exists()
|
|
201
|
-
? (userDocSnapshot.data() as IUserRecord)
|
|
202
|
-
: authUser
|
|
203
|
-
? { title: authUser.displayName || authUser.email || authUser.uid }
|
|
204
|
-
: null;
|
|
205
|
-
|
|
206
|
-
this.userState$.next({
|
|
207
|
-
...authState,
|
|
208
|
-
record: userRecord,
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
private initUserRecordFromAuthUser(authUser: ISneatAuthUser): void {
|
|
213
|
-
let request: IInitUserRecordRequest = {
|
|
214
|
-
email: authUser.email || undefined,
|
|
215
|
-
emailIsVerified: authUser.emailVerified,
|
|
216
|
-
authProvider: authUser?.providerId,
|
|
217
|
-
};
|
|
218
|
-
if (authUser?.displayName) {
|
|
219
|
-
request = { ...request, names: { fullName: authUser.displayName } };
|
|
220
|
-
}
|
|
221
|
-
this.userRecordService.initUserRecord(request).subscribe({
|
|
222
|
-
next: (_userDto) => {
|
|
223
|
-
// User record created successfully - no additional action needed
|
|
224
|
-
},
|
|
225
|
-
error: this.errorLogger.logErrorHandler('failed to create user record'),
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
private onUserSignedOut(): void {
|
|
230
|
-
this.uid = undefined;
|
|
231
|
-
this.unsubscribeFromUserDoc('onUserSignedOut()');
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// private createUserRecord(userDocRef: DocumentReference, authUser: ISneatAuthUser): void {
|
|
235
|
-
// if (this.userState$.value) {
|
|
236
|
-
// return;
|
|
237
|
-
// }
|
|
238
|
-
// this.db.firestore.runTransaction(async tx => {
|
|
239
|
-
// if (this.userState$.value) {
|
|
240
|
-
// return undefined;
|
|
241
|
-
// }
|
|
242
|
-
// const u = await tx.get(userDocRef);
|
|
243
|
-
// if (!u.exists) {
|
|
244
|
-
// const title = authUser.displayName || authUser.email || authUser.uid;
|
|
245
|
-
// const user: IUserRecord = authUser.email
|
|
246
|
-
// ? {title, email: authUser.email, emailVerified: authUser.emailVerified}
|
|
247
|
-
// : {title};
|
|
248
|
-
// await tx.set(userDocRef, user);
|
|
249
|
-
// return user;
|
|
250
|
-
// }
|
|
251
|
-
// return undefined;
|
|
252
|
-
// }).then(user => {
|
|
253
|
-
// if (user) {
|
|
254
|
-
// console.log('user record created:', user);
|
|
255
|
-
// }
|
|
256
|
-
// if (!this.userState$.value) {
|
|
257
|
-
// const userState: ISneatUserState = {
|
|
258
|
-
// status: AuthStatuses.authenticated,
|
|
259
|
-
// record: user,
|
|
260
|
-
// user: authUser,
|
|
261
|
-
// };
|
|
262
|
-
// this.userState$.next(userState);
|
|
263
|
-
// }
|
|
264
|
-
// }).catch(this.errorLogger.logErrorHandler('failed to create user record'));
|
|
265
|
-
// }
|
|
266
|
-
}
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { TestBed } from '@angular/core/testing';
|
|
2
|
-
import { SneatApiService } from '@sneat/api';
|
|
3
|
-
import { UserRecordService } from './user-record.service';
|
|
4
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
5
|
-
import { of, firstValueFrom } from 'rxjs';
|
|
6
|
-
import { IUserDbo } from '@sneat/dto';
|
|
7
|
-
|
|
8
|
-
describe('UserRecordService', () => {
|
|
9
|
-
let service: UserRecordService;
|
|
10
|
-
let sneatApiServiceMock: { post: ReturnType<typeof vi.fn> };
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
sneatApiServiceMock = {
|
|
14
|
-
post: vi.fn(),
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
TestBed.configureTestingModule({
|
|
18
|
-
providers: [
|
|
19
|
-
UserRecordService,
|
|
20
|
-
{
|
|
21
|
-
provide: SneatApiService,
|
|
22
|
-
useValue: sneatApiServiceMock,
|
|
23
|
-
},
|
|
24
|
-
],
|
|
25
|
-
});
|
|
26
|
-
service = TestBed.inject(UserRecordService);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('should be created', () => {
|
|
30
|
-
expect(service).toBeTruthy();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should initialize user record with provided request', async () => {
|
|
34
|
-
const request = {
|
|
35
|
-
authProvider: 'google.com',
|
|
36
|
-
email: 'test@example.com',
|
|
37
|
-
emailIsVerified: true,
|
|
38
|
-
names: { fullName: 'Test User' },
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const mockResponse: IUserDbo = {
|
|
42
|
-
id: 'user123',
|
|
43
|
-
title: 'Test User',
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
sneatApiServiceMock.post.mockReturnValue(of(mockResponse));
|
|
47
|
-
|
|
48
|
-
const result = await firstValueFrom(service.initUserRecord(request));
|
|
49
|
-
|
|
50
|
-
expect(result).toEqual(mockResponse);
|
|
51
|
-
expect(sneatApiServiceMock.post).toHaveBeenCalledWith(
|
|
52
|
-
'users/init_user_record',
|
|
53
|
-
expect.objectContaining({
|
|
54
|
-
authProvider: 'google.com',
|
|
55
|
-
email: 'test@example.com',
|
|
56
|
-
emailIsVerified: true,
|
|
57
|
-
names: { fullName: 'Test User' },
|
|
58
|
-
ianaTimezone: expect.any(String),
|
|
59
|
-
}),
|
|
60
|
-
);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should add ianaTimezone if not provided', async () => {
|
|
64
|
-
const request = {
|
|
65
|
-
email: 'test@example.com',
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const mockResponse: IUserDbo = {
|
|
69
|
-
id: 'user123',
|
|
70
|
-
title: 'Test User',
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
sneatApiServiceMock.post.mockReturnValue(of(mockResponse));
|
|
74
|
-
|
|
75
|
-
await firstValueFrom(service.initUserRecord(request));
|
|
76
|
-
|
|
77
|
-
const callArgs = sneatApiServiceMock.post.mock.calls[0][1];
|
|
78
|
-
expect(callArgs.ianaTimezone).toBeDefined();
|
|
79
|
-
expect(typeof callArgs.ianaTimezone).toBe('string');
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('should preserve provided ianaTimezone', async () => {
|
|
83
|
-
const request = {
|
|
84
|
-
email: 'test@example.com',
|
|
85
|
-
ianaTimezone: 'America/New_York',
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
const mockResponse: IUserDbo = {
|
|
89
|
-
id: 'user123',
|
|
90
|
-
title: 'Test User',
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
sneatApiServiceMock.post.mockReturnValue(of(mockResponse));
|
|
94
|
-
|
|
95
|
-
await firstValueFrom(service.initUserRecord(request));
|
|
96
|
-
|
|
97
|
-
expect(sneatApiServiceMock.post).toHaveBeenCalledWith(
|
|
98
|
-
'users/init_user_record',
|
|
99
|
-
expect.objectContaining({
|
|
100
|
-
ianaTimezone: 'America/New_York',
|
|
101
|
-
}),
|
|
102
|
-
);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('should exclude undefined values from request', async () => {
|
|
106
|
-
const request = {
|
|
107
|
-
email: 'test@example.com',
|
|
108
|
-
gender: undefined,
|
|
109
|
-
ageGroup: undefined,
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const mockResponse: IUserDbo = {
|
|
113
|
-
id: 'user123',
|
|
114
|
-
title: 'Test User',
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
sneatApiServiceMock.post.mockReturnValue(of(mockResponse));
|
|
118
|
-
|
|
119
|
-
await firstValueFrom(service.initUserRecord(request));
|
|
120
|
-
|
|
121
|
-
const callArgs = sneatApiServiceMock.post.mock.calls[0][1];
|
|
122
|
-
expect(callArgs.gender).toBeUndefined();
|
|
123
|
-
expect(callArgs.ageGroup).toBeUndefined();
|
|
124
|
-
expect(callArgs.email).toBe('test@example.com');
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('should share the observable for multiple subscribers', async () => {
|
|
128
|
-
const request = {
|
|
129
|
-
email: 'test@example.com',
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
const mockResponse: IUserDbo = {
|
|
133
|
-
id: 'user123',
|
|
134
|
-
title: 'Test User',
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
sneatApiServiceMock.post.mockReturnValue(of(mockResponse));
|
|
138
|
-
|
|
139
|
-
const obs = service.initUserRecord(request);
|
|
140
|
-
|
|
141
|
-
await Promise.all([firstValueFrom(obs), firstValueFrom(obs)]);
|
|
142
|
-
|
|
143
|
-
expect(sneatApiServiceMock.post).toHaveBeenCalledTimes(1);
|
|
144
|
-
});
|
|
145
|
-
});
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { Injectable, inject } from '@angular/core';
|
|
2
|
-
import { SneatApiService } from '@sneat/api';
|
|
3
|
-
import { IPersonNames } from '@sneat/auth-models';
|
|
4
|
-
import { AgeGroupID, excludeUndefined, Gender, SpaceType } from '@sneat/core';
|
|
5
|
-
import { IUserDbo } from '@sneat/dto';
|
|
6
|
-
import { Observable, share } from 'rxjs';
|
|
7
|
-
|
|
8
|
-
@Injectable({ providedIn: 'root' })
|
|
9
|
-
export class UserRecordService {
|
|
10
|
-
private readonly sneatApiService = inject(SneatApiService);
|
|
11
|
-
|
|
12
|
-
private initUserRecord$?: Observable<IUserDbo>;
|
|
13
|
-
|
|
14
|
-
public initUserRecord(request: IInitUserRecordRequest): Observable<IUserDbo> {
|
|
15
|
-
if (!request.ianaTimezone) {
|
|
16
|
-
request = {
|
|
17
|
-
...request,
|
|
18
|
-
ianaTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
request = excludeUndefined(request);
|
|
22
|
-
this.initUserRecord$ = this.sneatApiService
|
|
23
|
-
.post<IUserDbo>('users/init_user_record', request)
|
|
24
|
-
.pipe(share());
|
|
25
|
-
return this.initUserRecord$;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface IInitUserRecordRequest {
|
|
30
|
-
readonly authProvider?: string;
|
|
31
|
-
readonly gender?: Gender;
|
|
32
|
-
readonly ageGroup?: AgeGroupID;
|
|
33
|
-
readonly names?: IPersonNames;
|
|
34
|
-
readonly email?: string;
|
|
35
|
-
readonly emailIsVerified?: boolean;
|
|
36
|
-
readonly ianaTimezone?: string;
|
|
37
|
-
readonly space?: { type: SpaceType; title: string };
|
|
38
|
-
}
|
package/src/test-setup.ts
DELETED
package/tsconfig.json
DELETED
package/tsconfig.lib.json
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../../tsconfig.lib.base.json",
|
|
3
|
-
"exclude": [
|
|
4
|
-
"vite.config.ts",
|
|
5
|
-
"vite.config.mts",
|
|
6
|
-
"vitest.config.ts",
|
|
7
|
-
"vitest.config.mts",
|
|
8
|
-
"src/**/*.test.ts",
|
|
9
|
-
"src/**/*.spec.ts",
|
|
10
|
-
"src/**/*.test.tsx",
|
|
11
|
-
"src/**/*.spec.tsx",
|
|
12
|
-
"src/**/*.test.js",
|
|
13
|
-
"src/**/*.spec.js",
|
|
14
|
-
"src/**/*.test.jsx",
|
|
15
|
-
"src/**/*.spec.jsx",
|
|
16
|
-
"src/test-setup.ts",
|
|
17
|
-
"src/lib/testing/**/*"
|
|
18
|
-
]
|
|
19
|
-
}
|
package/tsconfig.lib.prod.json
DELETED
package/tsconfig.spec.json
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "./tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"outDir": "../../../dist/out-tsc",
|
|
5
|
-
"types": [
|
|
6
|
-
"vitest/globals",
|
|
7
|
-
"vitest/importMeta",
|
|
8
|
-
"vite/client",
|
|
9
|
-
"node",
|
|
10
|
-
"vitest"
|
|
11
|
-
]
|
|
12
|
-
},
|
|
13
|
-
"include": [
|
|
14
|
-
"vite.config.ts",
|
|
15
|
-
"vite.config.mts",
|
|
16
|
-
"vitest.config.ts",
|
|
17
|
-
"vitest.config.mts",
|
|
18
|
-
"src/**/*.test.ts",
|
|
19
|
-
"src/**/*.spec.ts",
|
|
20
|
-
"src/**/*.test.tsx",
|
|
21
|
-
"src/**/*.spec.tsx",
|
|
22
|
-
"src/**/*.test.js",
|
|
23
|
-
"src/**/*.spec.js",
|
|
24
|
-
"src/**/*.test.jsx",
|
|
25
|
-
"src/**/*.spec.jsx",
|
|
26
|
-
"src/**/*.d.ts"
|
|
27
|
-
],
|
|
28
|
-
"files": [
|
|
29
|
-
"src/test-setup.ts"
|
|
30
|
-
]
|
|
31
|
-
}
|
package/vite.config.mts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/// <reference types='vitest' />
|
|
2
|
-
import { defineConfig } from 'vitest/config';
|
|
3
|
-
import { createBaseViteConfig } from '../../../vite.config.base';
|
|
4
|
-
|
|
5
|
-
export default defineConfig(() =>
|
|
6
|
-
createBaseViteConfig({
|
|
7
|
-
dirname: __dirname,
|
|
8
|
-
name: 'auth-core',
|
|
9
|
-
}),
|
|
10
|
-
);
|
|
File without changes
|
|
File without changes
|