Kaynağa Gözat

feat(admin-ui): Move all local state to apollo cache

Michael Bromley 7 yıl önce
ebeveyn
işleme
9afd921063

+ 3 - 6
admin-ui/src/app/app.component.ts

@@ -1,9 +1,7 @@
 import { Component, OnInit } from '@angular/core';
 import { Observable } from 'rxjs';
-import { map, tap } from 'rxjs/operators';
-
+import { map } from 'rxjs/operators';
 import { DataService } from './data/providers/data.service';
-import { StateStore } from './state/state-store.service';
 
 @Component({
     selector: 'vdr-root',
@@ -17,9 +15,8 @@ export class AppComponent implements OnInit {
     }
 
     ngOnInit() {
-        this.loading$ = this.dataService.client.inFlightRequests().pipe(
-            tap(val => console.log('inFlightRequests:', val)),
-            map(count => 0 < count),
+        this.loading$ = this.dataService.client.getNetworkStatus().stream$.pipe(
+            map(data => 0 < data.networkStatus.inFlightRequests),
         );
     }
 }

+ 1 - 1
admin-ui/src/app/catalog/components/product-detail/product-detail.component.ts

@@ -17,7 +17,7 @@ export class ProductDetailComponent implements OnInit {
                 private route: ActivatedRoute) { }
 
     ngOnInit() {
-        this.product$ = this.dataService.product.getProduct(this.route.snapshot.paramMap.get('id'));
+        this.product$ = this.dataService.product.getProduct(this.route.snapshot.paramMap.get('id')).single$;
     }
 
 }

+ 13 - 7
admin-ui/src/app/catalog/components/product-list/product-list.component.ts

@@ -1,34 +1,40 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnDestroy, OnInit } from '@angular/core';
 import { QueryRef } from 'apollo-angular';
-import { Observable } from 'rxjs';
-import { map, tap } from 'rxjs/operators';
-
-import { GetProductList, GetProductListVariables } from '../../../data/types/gql-generated-types';
+import { Observable, Subject } from 'rxjs';
+import { map, takeUntil, tap } from 'rxjs/operators';
 import { DataService } from '../../../data/providers/data.service';
+import { GetProductList, GetProductListVariables } from '../../../data/types/gql-generated-types';
 
 @Component({
     selector: 'vdr-products-list',
     templateUrl: './product-list.component.html',
     styleUrls: ['./product-list.component.scss'],
 })
-export class ProductListComponent implements OnInit {
+export class ProductListComponent implements OnInit, OnDestroy {
 
     products$: Observable<any[]>;
     totalItems: number;
     itemsPerPage = 25;
     currentPage = 1;
     private productsQuery: QueryRef<GetProductList, GetProductListVariables>;
+    private destroy$ = new Subject<void>();
 
     constructor(private dataService: DataService) { }
 
     ngOnInit() {
-        this.productsQuery = this.dataService.product.getProducts(this.itemsPerPage, 0);
+        this.productsQuery = this.dataService.product.getProducts(this.itemsPerPage, 0).ref;
         this.products$ = this.productsQuery.valueChanges.pipe(
+            takeUntil(this.destroy$),
             tap(val => { this.totalItems = val.data.products.totalItems; }),
             map(val => val.data.products.items),
         );
     }
 
+    ngOnDestroy() {
+        this.destroy$.next();
+        this.destroy$.complete();
+    }
+
     getPage(pageNumber: number): void {
         const take = this.itemsPerPage;
         const skip = (pageNumber - 1) * this.itemsPerPage;

+ 3 - 4
admin-ui/src/app/core/components/app-shell/app-shell.component.ts

@@ -1,9 +1,8 @@
 import { Component, OnInit } from '@angular/core';
 import { Router } from '@angular/router';
 import { Observable } from 'rxjs';
-
 import { StateStore } from '../../../state/state-store.service';
-import { UserActions } from '../../../state/user/user-actions';
+import { AuthService } from '../../providers/auth/auth.service';
 
 @Component({
     selector: 'vdr-app-shell',
@@ -15,7 +14,7 @@ export class AppShellComponent implements OnInit {
     userName$: Observable<string>;
 
     constructor(private store: StateStore,
-                private userActions: UserActions,
+                private authService: AuthService,
                 private router: Router) { }
 
     ngOnInit() {
@@ -23,7 +22,7 @@ export class AppShellComponent implements OnInit {
     }
 
     logOut() {
-        this.userActions.logOut();
+        this.authService.logOut();
         this.router.navigate(['/login']);
     }
 

+ 3 - 2
admin-ui/src/app/core/core.module.ts

@@ -1,14 +1,14 @@
 import { NgModule } from '@angular/core';
+import { DataModule } from '../data/data.module';
 import { SharedModule } from '../shared/shared.module';
 import { StateModule } from '../state/state.module';
-
-import { DataModule } from '../data/data.module';
 import { AppShellComponent } from './components/app-shell/app-shell.component';
 import { BreadcrumbComponent } from './components/breadcrumb/breadcrumb.component';
 import { MainNavComponent } from './components/main-nav/main-nav.component';
 import { NotificationComponent } from './components/notification/notification.component';
 import { OverlayHostComponent } from './components/overlay-host/overlay-host.component';
 import { UserMenuComponent } from './components/user-menu/user-menu.component';
+import { AuthService } from './providers/auth/auth.service';
 import { AuthGuard } from './providers/guard/auth.guard';
 import { LocalStorageService } from './providers/local-storage/local-storage.service';
 import { NotificationService } from './providers/notification/notification.service';
@@ -27,6 +27,7 @@ import { OverlayHostService } from './providers/overlay-host/overlay-host.servic
     providers: [
         LocalStorageService,
         AuthGuard,
+        AuthService,
         OverlayHostService,
         NotificationService,
     ],

+ 71 - 0
admin-ui/src/app/core/providers/auth/auth.service.ts

@@ -0,0 +1,71 @@
+import { Injectable } from '@angular/core';
+import { Observable, of } from 'rxjs';
+import { catchError, map, mergeMap, switchMap } from 'rxjs/operators';
+import { DataService } from '../../../data/providers/data.service';
+import { LogIn } from '../../../data/types/gql-generated-types';
+import { LocalStorageService } from '../local-storage/local-storage.service';
+
+/**
+ * This service handles logic relating to authentication of the current user.
+ */
+@Injectable()
+export class AuthService {
+
+    constructor(private localStorageService: LocalStorageService,
+                private dataService: DataService) {}
+
+    /**
+     * Attempts to log in via the REST login endpoint and updates the app
+     * state on success.
+     */
+    logIn(username: string, password: string): Observable<LogIn> {
+        return this.dataService.user.attemptLogin(username, password).pipe(
+            switchMap(response => {
+                this.localStorageService.set('authToken', response.token);
+                return this.dataService.client.loginSuccess(username);
+            }),
+        );
+    }
+
+    /**
+     * Update the user status to being logged out.
+     */
+    logOut(): void {
+        this.dataService.client.logOut();
+        this.localStorageService.remove('authToken');
+    }
+
+    /**
+     * Checks the app state to see if the user is already logged in,
+     * and if not, attempts to validate any auth token found.
+     */
+    checkAuthenticatedStatus(): Observable<boolean> {
+        return this.dataService.client.userStatus().single$.pipe(
+            mergeMap(data => {
+                if (!data.userStatus.isLoggedIn) {
+                    return this.validateAuthToken();
+                } else {
+                    return of(true);
+                }
+            }),
+        );
+    }
+
+    /**
+     * Checks for an auth token and if found, attempts to validate
+     * that token against the API.
+     */
+    validateAuthToken(): Observable<boolean> {
+        if (!this.localStorageService.get('authToken')) {
+            return of(false);
+        }
+
+        return this.dataService.user.checkLoggedIn().pipe(
+            map(result => {
+                this.dataService.client.loginSuccess(result.identifier);
+                return true;
+            }),
+            catchError(err => of(false)),
+        );
+    }
+}

+ 9 - 15
admin-ui/src/app/core/providers/guard/auth.guard.ts

@@ -1,28 +1,22 @@
 import { Injectable } from '@angular/core';
 import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
-import { Observable, of } from 'rxjs';
-import { flatMap, mergeMap, tap } from 'rxjs/operators';
-
-import { StateStore } from '../../../state/state-store.service';
-import { UserActions } from '../../../state/user/user-actions';
+import { Observable } from 'rxjs';
+import { tap } from 'rxjs/operators';
+import { AuthService } from '../auth/auth.service';
 
+/**
+ * This guard prevents unauthorized users from accessing any routes which require
+ * authorization.
+ */
 @Injectable()
 export class AuthGuard implements CanActivate {
 
     constructor(private router: Router,
-                private userActions: UserActions,
-                private store: StateStore) {}
+                private authService: AuthService) {}
 
     canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
 
-        return this.store.select(state => state.user.isLoggedIn).pipe(
-            mergeMap(loggedIn => {
-                if (!loggedIn) {
-                    return this.userActions.checkAuth();
-                } else {
-                    return of(true);
-                }
-            }),
+        return this.authService.checkAuthenticatedStatus().pipe(
             tap(authenticated => {
                 if (authenticated) {
                     return true;

+ 14 - 0
admin-ui/src/app/data/client-state/client-defaults.ts

@@ -0,0 +1,14 @@
+import { GetNetworkStatus, GetUserStatus } from '../types/gql-generated-types';
+
+export const clientDefaults: GetNetworkStatus & GetUserStatus = {
+    networkStatus: {
+        inFlightRequests: 0,
+        __typename: 'NetworkStatus',
+    },
+    userStatus: {
+        username: '',
+        isLoggedIn: false,
+        loginTime: '',
+        __typename: 'UserStatus',
+    },
+};

+ 71 - 0
admin-ui/src/app/data/client-state/client-resolvers.ts

@@ -0,0 +1,71 @@
+import { InMemoryCache } from 'apollo-cache-inmemory';
+import { GraphQLFieldResolver } from 'graphql';
+import { GET_NEWTORK_STATUS } from '../queries/local-queries';
+import {
+    GetNetworkStatus,
+    GetUserStatus,
+    GetUserStatus_userStatus,
+    LogInVariables,
+    RequestStarted,
+} from '../types/gql-generated-types';
+
+export type ResolverContext = {
+    cache: InMemoryCache;
+    optimisticResponse: any;
+    getCacheKey: (storeObj: any) => string;
+};
+
+export type ResolverDefinition = {
+    Mutation: {
+        [name: string]: GraphQLFieldResolver<any, ResolverContext>;
+    },
+};
+
+export const clientResolvers: ResolverDefinition = {
+    Mutation: {
+        requestStarted: (_, args, { cache }): number => {
+            return updateRequestsInFlight(cache, 1);
+        },
+        requestCompleted: (_, args, { cache }): number => {
+            return updateRequestsInFlight(cache, -1);
+        },
+        logIn: (_, args: LogInVariables, { cache }): GetUserStatus_userStatus => {
+            const { username, loginTime } = args;
+            const data: GetUserStatus = {
+                userStatus: {
+                    __typename: 'UserStatus',
+                    username,
+                    loginTime,
+                    isLoggedIn: true,
+                },
+            };
+            cache.writeData({ data });
+            return data.userStatus;
+        },
+        logOut: (_, args, { cache }): GetUserStatus_userStatus => {
+            const data: GetUserStatus = {
+                userStatus: {
+                    __typename: 'UserStatus',
+                    username: '',
+                    loginTime: '',
+                    isLoggedIn: false,
+                },
+            };
+            cache.writeData({ data });
+            return data.userStatus;
+        },
+    },
+};
+
+function updateRequestsInFlight(cache: InMemoryCache, increment: 1 | -1): number {
+    const previous = cache.readQuery<GetNetworkStatus>({ query: GET_NEWTORK_STATUS });
+    const inFlightRequests = previous.networkStatus.inFlightRequests + increment;
+    const data: GetNetworkStatus = {
+        networkStatus: {
+            __typename: 'NetworkStatus',
+            inFlightRequests,
+        },
+    };
+    cache.writeData({ data });
+    return inFlightRequests;
+}

+ 14 - 38
admin-ui/src/app/data/data.module.ts

@@ -1,58 +1,30 @@
 import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
 import { NgModule } from '@angular/core';
+import { APOLLO_OPTIONS, ApolloModule } from 'apollo-angular';
 import { HttpLink, HttpLinkModule } from 'apollo-angular-link-http';
 import { InMemoryCache } from 'apollo-cache-inmemory';
 import { ApolloLink } from 'apollo-link';
-
-import { APOLLO_OPTIONS, ApolloModule } from 'apollo-angular';
 import { withClientState } from 'apollo-link-state';
-import gql from 'graphql-tag';
 import { API_PATH } from '../../../../shared/shared-constants';
+import { environment } from '../../environments/environment';
 import { API_URL } from '../app.config';
+import { clientDefaults } from './client-state/client-defaults';
+import { clientResolvers } from './client-state/client-resolvers';
 import { BaseDataService } from './providers/base-data.service';
 import { DataService } from './providers/data.service';
 import { DefaultInterceptor } from './providers/interceptor';
-import { GET_IN_FLIGHT_REQUESTS } from './queries/local-queries';
 
-// This is the same cache you pass into new ApolloClient
 const apolloCache = new InMemoryCache();
 
-(window as any)['apolloCache'] = apolloCache;
+if (!environment.production) {
+    // make the Apollo Cache inspectable in the console for debug purposes
+    (window as any)['apolloCache'] = apolloCache;
+}
 
 const stateLink = withClientState({
     cache: apolloCache,
-    resolvers: {
-        Mutation: {
-            requestStarted: (_, __, { cache }) => {
-                const previous = cache.readQuery({ query: GET_IN_FLIGHT_REQUESTS });
-                const data = {
-                    network: {
-                        __typename: 'Network',
-                        inFlightRequests: previous.network.inFlightRequests + 1,
-                    },
-                };
-                cache.writeData({ data });
-                return null;
-            },
-            requestCompleted: (_, __, { cache }) => {
-                const previous = cache.readQuery({ query: GET_IN_FLIGHT_REQUESTS });
-                const data = {
-                    network: {
-                        __typename: 'Network',
-                        inFlightRequests: previous.network.inFlightRequests - 1,
-                    },
-                };
-                cache.writeData({ data });
-                return null;
-            },
-        },
-    },
-    defaults: {
-        network: {
-            inFlightRequests: 0,
-            __typename: 'Network',
-        },
-    },
+    resolvers: clientResolvers,
+    defaults: clientDefaults,
 });
 
 export function createApollo(httpLink: HttpLink) {
@@ -62,6 +34,10 @@ export function createApollo(httpLink: HttpLink) {
     };
 }
 
+/**
+ * The DataModule is responsible for all API calls *and* serves as the source of truth for global app
+ * state via the apollo-link-state package.
+ */
 @NgModule({
     imports: [
         ApolloModule,

+ 33 - 0
admin-ui/src/app/data/mutations/local-mutations.ts

@@ -0,0 +1,33 @@
+import gql from 'graphql-tag';
+
+export const REQUEST_STARTED = gql`
+    mutation RequestStarted {
+        requestStarted @client
+    }
+`;
+
+export const REQUEST_COMPLETED = gql`
+    mutation RequestCompleted {
+        requestCompleted @client
+    }
+`;
+
+export const LOG_IN = gql`
+    mutation LogIn($username: String!, $loginTime: String!) {
+        logIn(username: $username, loginTime: $loginTime) @client {
+            username
+            isLoggedIn
+            loginTime
+        }
+    }
+`;
+
+export const LOG_OUT = gql`
+    mutation LogOut {
+        logOut @client {
+            username
+            isLoggedIn
+            loginTime
+        }
+    }
+`;

+ 12 - 8
admin-ui/src/app/data/providers/base-data.service.ts

@@ -1,13 +1,12 @@
 import { HttpClient } from '@angular/common/http';
 import { Injectable } from '@angular/core';
-import { Apollo, QueryRef } from 'apollo-angular';
+import { Apollo } from 'apollo-angular';
 import { DocumentNode } from 'graphql/language/ast';
 import { Observable } from 'rxjs';
 import { map } from 'rxjs/operators';
-
 import { API_URL } from '../../app.config';
 import { LocalStorageService } from '../../core/providers/local-storage/local-storage.service';
-import { FetchResult } from 'apollo-link';
+import { QueryResult } from '../types/query-result';
 
 @Injectable()
 export class BaseDataService {
@@ -17,10 +16,10 @@ export class BaseDataService {
                 private localStorageService: LocalStorageService) {}
 
     /**
-     * Performs a GraphQL query
+     * Performs a GraphQL watch query
      */
-    query<T, V = Record<string, any>>(query: DocumentNode, variables?: V): QueryRef<T> {
-        return this.apollo.watchQuery<T, V>({
+    query<T, V = Record<string, any>>(query: DocumentNode, variables?: V): QueryResult<T, V> {
+        const queryRef = this.apollo.watchQuery<T, V>({
             query,
             variables,
             context: {
@@ -29,10 +28,15 @@ export class BaseDataService {
                 },
             },
         });
+        return new QueryResult<T, any>(queryRef);
     }
 
-    mutate(mutation: DocumentNode): Observable<FetchResult> {
-        return this.apollo.mutate({ mutation });
+    /**
+     * Performs a GraphQL mutation
+     */
+    mutate<T, V = Record<string, any>>(mutation: DocumentNode, variables?: V): Observable<T> {
+        return this.apollo.mutate<T, V>({ mutation, variables }).pipe(
+            map(result => result.data as T));
     }
 
     /**

+ 25 - 26
admin-ui/src/app/data/providers/client-data.service.ts

@@ -1,40 +1,39 @@
 import { Observable } from 'rxjs';
-
-import gql from 'graphql-tag';
-import { map } from 'rxjs/operators';
+import { LOG_IN, LOG_OUT, REQUEST_COMPLETED, REQUEST_STARTED } from '../mutations/local-mutations';
+import { GET_NEWTORK_STATUS, GET_USER_STATUS } from '../queries/local-queries';
+import { GetNetworkStatus, GetUserStatus, LogIn, LogInVariables, LogOut, RequestCompleted, RequestStarted } from '../types/gql-generated-types';
+import { QueryResult } from '../types/query-result';
 import { BaseDataService } from './base-data.service';
 
 export class ClientDataService {
 
     constructor(private baseDataService: BaseDataService) {}
 
-    startRequest() {
-        return this.baseDataService.mutate(gql`
-            mutation {
-                requestStarted @client
-            }
-        `);
+    startRequest(): Observable<RequestStarted> {
+        return this.baseDataService.mutate<RequestStarted>(REQUEST_STARTED);
     }
 
-    completeRequest() {
-        return this.baseDataService.mutate(gql`
-            mutation {
-                requestCompleted @client
-            }
-        `);
+    completeRequest(): Observable<RequestCompleted> {
+        return this.baseDataService.mutate<RequestCompleted>(REQUEST_COMPLETED);
     }
 
-    inFlightRequests(): Observable<number> {
-        return this.baseDataService.query<any>(gql`
-            query {
-                network @client {
-                    inFlightRequests
-                }
-            }
-        `).valueChanges.pipe(
-            map(result => result.data.network.inFlightRequests),
-        );
+    getNetworkStatus(): QueryResult<GetNetworkStatus> {
+        return this.baseDataService.query<GetNetworkStatus>(GET_NEWTORK_STATUS);
     }
 
-}
+    loginSuccess(username: string): Observable<LogIn> {
+        return this.baseDataService.mutate<LogIn, LogInVariables>(LOG_IN, {
+            username,
+            loginTime: Date.now().toString(),
+        });
+    }
+
+    logOut(): Observable<LogOut> {
+        return this.baseDataService.mutate(LOG_OUT);
+    }
+
+    userStatus(): QueryResult<GetUserStatus> {
+        return this.baseDataService.query<GetUserStatus>(GET_USER_STATUS);
+    }
 
+}

+ 7 - 6
admin-ui/src/app/data/providers/product-data.service.ts

@@ -1,23 +1,24 @@
-import { QueryRef } from 'apollo-angular';
-
 import { ID } from '../../../../../shared/shared-types';
 import { GET_PRODUCT_BY_ID, GET_PRODUCT_LIST } from '../queries/product-queries';
 import { GetProductById, GetProductByIdVariables, GetProductList, GetProductListVariables, LanguageCode } from '../types/gql-generated-types';
-
+import { QueryResult } from '../types/query-result';
 import { BaseDataService } from './base-data.service';
 
 export class ProductDataService {
 
     constructor(private baseDataService: BaseDataService) {}
 
-    getProducts(take: number = 10, skip: number = 0): QueryRef<GetProductList, GetProductListVariables> {
+    getProducts(take: number = 10, skip: number = 0): QueryResult<GetProductList, GetProductListVariables> {
         return this.baseDataService
             .query<GetProductList, GetProductListVariables>(GET_PRODUCT_LIST, { take, skip, languageCode: LanguageCode.en });
     }
 
-    getProduct(id: ID): any {
+    getProduct(id: ID): QueryResult<GetProductById> {
         const stringId = id.toString();
-        return this.baseDataService.query<GetProductById, GetProductByIdVariables>(GET_PRODUCT_BY_ID, { id: stringId });
+        return this.baseDataService.query<GetProductById, GetProductByIdVariables>(GET_PRODUCT_BY_ID, {
+            id: stringId,
+            languageCode: LanguageCode.en,
+        });
     }
 
 }

+ 1 - 1
admin-ui/src/app/data/providers/user-data.service.ts

@@ -12,7 +12,7 @@ export class UserDataService {
         return this.baseDataService.get('auth/me');
     }
 
-    logIn(username: string, password: string): Observable<LoginResponse> {
+    attemptLogin(username: string, password: string): Observable<LoginResponse> {
         return this.baseDataService.post('auth/login', {
             username,
             password,

+ 13 - 3
admin-ui/src/app/data/queries/local-queries.ts

@@ -1,9 +1,19 @@
 import gql from 'graphql-tag';
 
-export const GET_IN_FLIGHT_REQUESTS = gql`
-    query GetInFlightRequests {
-        network @client {
+export const GET_NEWTORK_STATUS = gql`
+    query GetNetworkStatus {
+        networkStatus @client {
             inFlightRequests
         }
     }
 `;
+
+export const GET_USER_STATUS = gql`
+    query GetUserStatus {
+        userStatus @client {
+            username
+            isLoggedIn
+            loginTime
+        }
+    }
+`;

+ 13 - 4
admin-ui/src/app/data/types/client-types.graphql

@@ -1,12 +1,21 @@
 type Query {
-    network: Network
+    networkStatus: NetworkStatus!
+    userStatus: UserStatus!
 }
 
 type Mutation {
-    requestStarted: Network
-    requestCompleted: Network
+    requestStarted: Int!
+    requestCompleted: Int!
+    logIn(username: String!, loginTime: String!): UserStatus
+    logOut: UserStatus
 }
 
-type Network {
+type NetworkStatus {
     inFlightRequests: Int!
 }
+
+type UserStatus {
+    username: String!
+    isLoggedIn: Boolean!
+    loginTime: String!
+}

+ 92 - 6
admin-ui/src/app/data/types/gql-generated-types.ts

@@ -4,16 +4,66 @@
 // This file was automatically generated and should not be edited.
 
 // ====================================================
-// GraphQL query operation: GetInFlightRequests
+// GraphQL mutation operation: RequestStarted
 // ====================================================
 
-export interface GetInFlightRequests_network {
-  __typename: "Network";
-  inFlightRequests: number;
+export interface RequestStarted {
+  requestStarted: number;
+}
+
+
+/* tslint:disable */
+// This file was automatically generated and should not be edited.
+
+// ====================================================
+// GraphQL mutation operation: RequestCompleted
+// ====================================================
+
+export interface RequestCompleted {
+  requestCompleted: number;
+}
+
+
+/* tslint:disable */
+// This file was automatically generated and should not be edited.
+
+// ====================================================
+// GraphQL mutation operation: LogIn
+// ====================================================
+
+export interface LogIn_logIn {
+  __typename: "UserStatus";
+  username: string;
+  isLoggedIn: boolean;
+  loginTime: string;
+}
+
+export interface LogIn {
+  logIn: LogIn_logIn | null;
 }
 
-export interface GetInFlightRequests {
-  network: GetInFlightRequests_network | null;
+export interface LogInVariables {
+  username: string;
+  loginTime: string;
+}
+
+
+/* tslint:disable */
+// This file was automatically generated and should not be edited.
+
+// ====================================================
+// GraphQL mutation operation: LogOut
+// ====================================================
+
+export interface LogOut_logOut {
+  __typename: "UserStatus";
+  username: string;
+  isLoggedIn: boolean;
+  loginTime: string;
+}
+
+export interface LogOut {
+  logOut: LogOut_logOut | null;
 }
 
 
@@ -84,6 +134,42 @@ export interface GetProductListVariables {
   languageCode?: LanguageCode | null;
 }
 
+
+/* tslint:disable */
+// This file was automatically generated and should not be edited.
+
+// ====================================================
+// GraphQL query operation: GetNetworkStatus
+// ====================================================
+
+export interface GetNetworkStatus_networkStatus {
+  __typename: "NetworkStatus";
+  inFlightRequests: number;
+}
+
+export interface GetNetworkStatus {
+  networkStatus: GetNetworkStatus_networkStatus;
+}
+
+
+/* tslint:disable */
+// This file was automatically generated and should not be edited.
+
+// ====================================================
+// GraphQL query operation: GetUserStatus
+// ====================================================
+
+export interface GetUserStatus_userStatus {
+  __typename: "UserStatus";
+  username: string;
+  isLoggedIn: boolean;
+  loginTime: string;
+}
+
+export interface GetUserStatus {
+  userStatus: GetUserStatus_userStatus;
+}
+
 /* tslint:disable */
 // This file was automatically generated and should not be edited.
 

+ 35 - 0
admin-ui/src/app/data/types/query-result.ts

@@ -0,0 +1,35 @@
+import { QueryRef } from 'apollo-angular';
+import { Observable } from 'rxjs';
+import { map, take } from 'rxjs/operators';
+
+/**
+ * This class wraps the Apollo Angular QueryRef object and exposes some getters
+ * for convenience.
+ */
+export class QueryResult<T, V = Record<string, any>> {
+    constructor(private queryRef: QueryRef<T, V>) {}
+
+    /**
+     * Returns an Observable which emits a single result and then completes.
+     */
+    get single$(): Observable<T> {
+        return this.queryRef.valueChanges.pipe(
+            take(1),
+            map(result => result.data),
+        );
+    }
+
+    /**
+     * Returns an Observable which emits until unsubscribed.
+     */
+    get stream$(): Observable<T> {
+        return this.queryRef.valueChanges.pipe(
+            map(result => result.data),
+        );
+    }
+
+    get ref(): QueryRef<T, V> {
+        return this.queryRef;
+    }
+
+}

+ 5 - 9
admin-ui/src/app/login/components/login/login.component.ts

@@ -1,9 +1,8 @@
 import { HttpErrorResponse } from '@angular/common/http';
-import { Component, OnInit } from '@angular/core';
+import { Component } from '@angular/core';
 import { Router } from '@angular/router';
-
 import { API_URL } from '../../../app.config';
-import { UserActions } from '../../../state/user/user-actions';
+import { AuthService } from '../../../core/providers/auth/auth.service';
 
 @Component({
     selector: 'vdr-login',
@@ -16,15 +15,13 @@ export class LoginComponent {
     password = '';
     lastError = '';
 
-    constructor(private userActions: UserActions,
+    constructor(private authService: AuthService,
                 private router: Router) { }
 
     logIn(): void {
-        this.userActions.logIn(this.username, this.password)
+        this.authService.logIn(this.username, this.password)
             .subscribe(
-                () => {
-                    this.router.navigate(['/']);
-                },
+                () => this.router.navigate(['/']),
                 (err: HttpErrorResponse) => {
                     switch (err.status) {
                         case 401:
@@ -36,7 +33,6 @@ export class LoginComponent {
                         default:
                             this.lastError = err.message;
                     }
-                    console.log(err);
                 });
     }
 

+ 1 - 1
admin-ui/src/app/state/user/user-actions.ts

@@ -38,7 +38,7 @@ export class UserActions {
     logIn(username: string, password: string): Observable<any> {
         this.store.dispatch(new Actions.Login());
 
-        return this.dataService.user.logIn(username, password).pipe(
+        return this.dataService.user.attemptLogin(username, password).pipe(
             map(result => {
                 this.store.dispatch(new Actions.LoginSuccess({
                     username,

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 4824
schema.json


Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor