diff --git a/projects/common/src/fm/common-service.module.ts b/projects/common/src/fm/common-service.module.ts index 3db913f..8f387a5 100644 --- a/projects/common/src/fm/common-service.module.ts +++ b/projects/common/src/fm/common-service.module.ts @@ -38,7 +38,10 @@ import { PackageService } from './services/package.service'; import { PackagePreloadStrategy } from './services/package-preload-strategy.service'; import { SenmlService } from './services/senml-service'; import { DeviceService } from './services/device.service'; -import { GradientService} from './services/gradient.service'; +import { GradientService } from './services/gradient.service'; +import { UserDataService } from './services/user-data.service'; +import { CacheService } from './services/cache-service'; +import { SharedItemService } from './services/shared-item.service'; export { FolderService, @@ -69,7 +72,10 @@ export { SenmlService, PackagePreloadStrategy, DeviceService, - GradientService + GradientService, + UserDataService, + CacheService, + SharedItemService }; @NgModule({ @@ -90,9 +96,9 @@ export class AppCommonServiceModule { providers: [ AppConfig, ItemTypeService, - provideAppInitializer(() => { - const initializerFn = (appConfigFactory)(inject(Injector), inject(AppConfig), inject(OAuthService), inject(AuthConfigFactory), inject(OAuthStorage), inject(ItemTypeService)); - return initializerFn(); + provideAppInitializer(() => { + const initializerFn = (appConfigFactory)(inject(Injector), inject(AppConfig), inject(OAuthService), inject(AuthConfigFactory), inject(OAuthStorage), inject(ItemTypeService)); + return initializerFn(); }), { provide: HTTP_INTERCEPTORS, diff --git a/projects/common/src/fm/common.module.ts b/projects/common/src/fm/common.module.ts index 9f17ace..177475f 100644 --- a/projects/common/src/fm/common.module.ts +++ b/projects/common/src/fm/common.module.ts @@ -61,6 +61,9 @@ import { IItemTask, ItemTask } from './models/itemTask'; import { IJsonline } from './models/json-line'; import { IListItem } from './models/list.item'; import { IPackage, IPackages } from './models/package'; +import { IAclRights } from './models/acl.rights'; +import { IListItemAclRights } from './models/list.item.acl.rights'; +import { ISharedItem } from './models/shared.item'; import { IQueryState } from './models/query.state'; import { ISenMLItem } from './models/senml-item'; import { ITypeaheadItem } from './models/typeahead.item'; @@ -79,6 +82,7 @@ export { commonReducers, EditImageModalComponent, GradientComponent, GradientSelectComponent, HasClaimDirective, HasPackageDirective, HasRoleDirective, IAuthconfigFactory, IColor, IDataLayer, IEventMessage, IGradientstop, IItem, IItemLinkType, IItemTask, IItemType, IItemTypes, IJsonline, IListItem, IPackage, + IAclRights, IListItemAclRights, ISharedItem, IPackages, IQueryState, ISenMLItem, Item, ItemLinkComponent, ItemTask, ITypeaheadItem, IUrlType, IUser, MenuBackgroundComponent, NotFoundComponent, NotImplementedComponent, PackageExistsDirective, ResumableFileUploadComponent, SafePipe, SecureOAuthStorage, SessionClearedComponent, SidePanelComponent, TagInputComponent, ThumbnailComponent, TimespanComponent, UserMenuComponent, WeatherCurrentObservation }; diff --git a/projects/common/src/fm/models/acl.rights.ts b/projects/common/src/fm/models/acl.rights.ts new file mode 100644 index 0000000..37309f6 --- /dev/null +++ b/projects/common/src/fm/models/acl.rights.ts @@ -0,0 +1,9 @@ +export interface IAclRights { + id?: number, + claim: any, + expires: Date, + rights: number, + owner: any, + deep: boolean +} + \ No newline at end of file diff --git a/projects/common/src/fm/models/list.item.acl.rights.ts b/projects/common/src/fm/models/list.item.acl.rights.ts new file mode 100644 index 0000000..32f6420 --- /dev/null +++ b/projects/common/src/fm/models/list.item.acl.rights.ts @@ -0,0 +1,6 @@ +import { IItem } from '@farmmaps/common'; +import { IAclRights } from './acl.rights'; + +export interface IListItemAclRights extends IItem { + aclRights: IAclRights[] +} \ No newline at end of file diff --git a/projects/common/src/fm/models/shared.item.ts b/projects/common/src/fm/models/shared.item.ts new file mode 100644 index 0000000..4158426 --- /dev/null +++ b/projects/common/src/fm/models/shared.item.ts @@ -0,0 +1,13 @@ +import { IUser } from '@farmmaps/common'; + +export interface ISharedItem { + code: string, + name: string, + type: string, + sharedToUser: IUser, + rights: Date, + expires: Date, + dataDate: Date, + childItems: ISharedItem[] + } + \ No newline at end of file diff --git a/projects/common/src/fm/services/cache-service.ts b/projects/common/src/fm/services/cache-service.ts new file mode 100644 index 0000000..2025eb9 --- /dev/null +++ b/projects/common/src/fm/services/cache-service.ts @@ -0,0 +1,44 @@ +import { Injectable } from '@angular/core'; +import { IItem, ItemService } from '@farmmaps/common'; +import { OAuthService } from 'angular-oauth2-oidc'; +import { Observable, ReplaySubject, Subscription, timer } from 'rxjs'; +import { catchError, take } from 'rxjs/operators'; + +const REFRESH_INTERVAL = 15 * 60 * 1000; // 15m + +@Injectable({ providedIn: 'root'}) +export class CacheService { + private proxyCacheMap: { [key: string]: ReplaySubject } = {}; + private subscriptionMap: { [key: string]: Subscription } = {}; + + constructor(private itemService: ItemService, public oauthService: OAuthService) { + timer(0, REFRESH_INTERVAL).subscribe(() => { + this.subscriptionMap = {}; + }) + } + + getItemList(itemType: string) : Observable { + if (!this.proxyCacheMap[itemType]) { + this.proxyCacheMap[itemType] = new ReplaySubject(1); + } + + if (this.oauthService.getAccessToken() != null && !this.subscriptionMap[itemType]) { + this.subscriptionMap[itemType] = this.itemService.getItemList(itemType) + .pipe( + catchError(error => { + this.subscriptionMap[itemType].unsubscribe(); + this.subscriptionMap[itemType] = null; + throw error; + }), + + ).subscribe(items => { + this.proxyCacheMap[itemType].next(items); + }); + } + + return this.proxyCacheMap[itemType].asObservable() + .pipe( + take(1) + ); + } +} diff --git a/projects/common/src/fm/services/shared-item.service.ts b/projects/common/src/fm/services/shared-item.service.ts new file mode 100644 index 0000000..c7ee375 --- /dev/null +++ b/projects/common/src/fm/services/shared-item.service.ts @@ -0,0 +1,73 @@ +import { HttpClient, HttpParams } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { AppConfig, IListItem } from '@farmmaps/common'; +import { Observable } from 'rxjs'; +import { IListItemAclRights } from '../models/list.item.acl.rights'; +import { ISharedItem } from '../models/shared.item'; + +@Injectable({ + providedIn: 'root' +}) +export class SharedItemService { + + constructor( + public httpClient: HttpClient, + public appConfig: AppConfig) { + } + + apiEndpoint() { + return this.appConfig.getConfig('apiEndPoint'); + } + + public getSharedUsersForItem(itemCode: string): Observable { + return this.httpClient.get(`${this.apiEndpoint()}/api/v1/items/${itemCode}/sharedusers`); + } + + getSharedItemsWithRightsInfo(userCode: string): Observable { + return this.httpClient.get(`${this.apiEndpoint()}/api/v1/users/${userCode}/shared`); + } + + shareItem(itemCode: string, userCode: string, userRights: string, expireTimespan: string, deep: boolean): Observable { + const body = { + rights: userRights, + expire: expireTimespan, + deep: deep + }; + + return this.httpClient.post(`${this.apiEndpoint()}/api/v1/items/${itemCode}/share/${userCode}`, body); + } + + revokeSharedItem(itemCode: string, userCode: string): Observable { + return this.httpClient.delete(`${this.apiEndpoint()}/api/v1/items/${itemCode}/share/${userCode}`); + } + + getSharedItemRights(sharedByMe: boolean): Observable { + let params = new HttpParams(); + params = params.append('byMe', sharedByMe); + + return this.httpClient.get(`${this.apiEndpoint()}/api/v1/user/rights`, {params: params}); + } + + getSharedItemsByParent(parentCode: string): Observable { + return this.httpClient.get(`${this.apiEndpoint()}/api/v1/items/${parentCode}/sharedchildren`); + } + + getSharedWithCurrentUser(profile: string): Observable { + let params = new HttpParams(); + if (profile != null) { + params = params.append('sharedBy', profile); + } + + return this.httpClient.get(`${this.apiEndpoint()}/api/v1/items/shared/`, {params: params}); + } + + // this method does not belong here, belongs in some sort of item service + public copyItem(itemCode: string, newParentCode: string, newUserCode: string): Observable { + const body = { + itemCode: itemCode, + newParentCode: newParentCode, + newUserCode: newUserCode + }; + return this.httpClient.post(`${this.apiEndpoint()}/api/v1/items/copy`, body); + } +} diff --git a/projects/common/src/fm/services/user-data.service.ts b/projects/common/src/fm/services/user-data.service.ts new file mode 100644 index 0000000..ddf96e0 --- /dev/null +++ b/projects/common/src/fm/services/user-data.service.ts @@ -0,0 +1,34 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { AppConfig, IUser } from '@farmmaps/common'; +import { Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class UserDataService { + constructor( + public httpClient: HttpClient, + public appConfig: AppConfig) { + } + + ApiEndpoint() { + return this.appConfig.getConfig('apiEndPoint'); + } + + loadUserNotifications(): Observable { + return this.httpClient.get(`${this.ApiEndpoint()}/api/v1/notifications`); + } + + readUserNotification(notificationCode: string): Observable { + return this.httpClient.put(`${this.ApiEndpoint()}/api/v1/notifications/${notificationCode}`, null); + } + + deleteUserNotification(notificationCode: string): Observable { + return this.httpClient.delete(`${this.ApiEndpoint()}/api/v1/notifications/${notificationCode}`); + } + + getUserConnections(userCode: string, active: boolean): Observable { + return this.httpClient.get(`${this.ApiEndpoint()}/api/v1/users/${userCode}/connections?active=${active}`); + } +}