Skip to content

Commit

Permalink
fix: allows resource related project member or admin to edit its prop…
Browse files Browse the repository at this point in the history
…erties (DEV-3723) (#1636)
  • Loading branch information
irmastnt authored Jun 6, 2024
1 parent 2cd96e5 commit a04bb41
Show file tree
Hide file tree
Showing 11 changed files with 161 additions and 24 deletions.
10 changes: 7 additions & 3 deletions libs/vre/shared/app-helper-services/src/lib/project.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { Constants, ReadUser } from '@dasch-swiss/dsp-js';
import { AppConfigService } from '@dasch-swiss/vre/shared/app-config';
import { AppConfigService, DspAppConfig } from '@dasch-swiss/vre/shared/app-config';

@Injectable({
providedIn: 'root',
Expand All @@ -23,8 +23,12 @@ export class ProjectService {
}

uuidToIri(uuid: string): string {
if (uuid && !uuid.startsWith(this._acs.dspAppConfig.iriBase)) {
return `${this._acs.dspAppConfig.iriBase}/projects/${uuid}`;
return ProjectService.uuidToIri(uuid, this._acs.dspAppConfig);
}

static uuidToIri(uuid: string, dspAppConfig: DspAppConfig): string {
if (uuid && !uuid.startsWith(dspAppConfig.iriBase)) {
return `${dspAppConfig.iriBase}/projects/${uuid}`;
}

return uuid;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { Inject, Injectable } from '@angular/core';
import { KnoraApiConnection, ReadResource, SystemPropertyDefinition } from '@dasch-swiss/dsp-js';
import { Common, DspResource } from '@dasch-swiss/vre/shared/app-common';
import { DspApiConnectionToken } from '@dasch-swiss/vre/shared/app-config';
import { SetCurrentResourceAction } from '@dasch-swiss/vre/shared/app-state';
import { Store } from '@ngxs/store';
import { BehaviorSubject, Subscription } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';

Expand All @@ -17,7 +19,8 @@ export class ResourceFetcherService {

constructor(
@Inject(DspApiConnectionToken)
private _dspApiConnection: KnoraApiConnection
private _dspApiConnection: KnoraApiConnection,
private _store: Store
) {}

onInit(resourceIri: string) {
Expand All @@ -44,6 +47,7 @@ export class ResourceFetcherService {

// gather system property information
res.systemProps = res.res.entityInfo.getPropertyDefinitionsByType(SystemPropertyDefinition);
this._store.dispatch(new SetCurrentResourceAction(res));
return res;
})
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { propertiesTypeMapping } from './resource-payloads-mapping';
mat-icon-button
(click)="addItem()"
*ngIf="
(isCurrentProjectAdminOrSysAdmin$ | async) &&
(isCurrentProjectAdminSysAdminOrMember$ | async) &&
(!propertyValueService.currentlyAdding || propertyValueService.keepEditMode) &&
(propertyValueService.formArray.controls.length === 0 ||
[Cardinality._0_n, Cardinality._1_n].includes(propertyValueService.cardinality)) &&
Expand All @@ -30,7 +30,8 @@ import { propertiesTypeMapping } from './resource-payloads-mapping';
export class PropertyValuesComponent implements OnInit {
@Input() itemTpl!: TemplateRef<any>;

@Select(ProjectsSelectors.isCurrentProjectAdminOrSysAdmin) isCurrentProjectAdminOrSysAdmin$!: Observable<boolean>;
@Select(ProjectsSelectors.isCurrentProjectAdminSysAdminOrMember)
isCurrentProjectAdminSysAdminOrMember$!: Observable<boolean>;

protected readonly Cardinality = Cardinality;

Expand Down
2 changes: 2 additions & 0 deletions libs/vre/shared/app-state/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,7 @@ export * from './lib/projects/projects.state';
export * from './lib/resource/resource.state';
export * from './lib/user/user.state';

export * from './lib/config.state';

export * from './lib/model-interfaces';
export * from './store.module';
32 changes: 32 additions & 0 deletions libs/vre/shared/app-state/src/lib/config.state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// state/config.state.ts
import { Injectable } from '@angular/core';
import { DspAppConfig } from '@dasch-swiss/vre/shared/app-config';
import { State, Action, StateContext, Selector } from '@ngxs/store';

export class SetConfigAction {
static readonly type = '[Config] Set';
constructor(public configStateModel: ConfigStateModel) {}
}

export interface ConfigStateModel {
dspAppConfig: DspAppConfig;
}

@State<ConfigStateModel>({
name: 'config',
defaults: {
dspAppConfig: {} as DspAppConfig,
},
})
@Injectable()
export class ConfigState {
@Action(SetConfigAction)
setConfig(ctx: StateContext<ConfigStateModel>, action: SetConfigAction) {
ctx.setState(action.configStateModel);
}

@Selector([ConfigState])
static getConfig(state: ConfigStateModel): DspAppConfig {
return state.dspAppConfig;
}
}
75 changes: 62 additions & 13 deletions libs/vre/shared/app-state/src/lib/projects/projects.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ import {
StoredProject,
} from '@dasch-swiss/dsp-js';
import { RestrictedViewResponse } from '@dasch-swiss/vre/open-api';
import { RouteConstants } from '@dasch-swiss/vre/shared/app-config';
import { DspResource } from '@dasch-swiss/vre/shared/app-common';
import { DspAppConfig, RouteConstants } from '@dasch-swiss/vre/shared/app-config';
import { ProjectService } from '@dasch-swiss/vre/shared/app-helper-services';
import { Selector } from '@ngxs/store';
import { ConfigState } from '../config.state';
import { IKeyValuePairs } from '../model-interfaces';
import { ResourceSelectors } from '../resource/resource.selectors';
import { RouterSelectors } from '../router/router.selector';
import { UserSelectors } from '../user/user.selectors';
import { ProjectsState } from './projects.state';
Expand Down Expand Up @@ -86,40 +89,86 @@ export class ProjectsSelectors {
return project;
}

@Selector([ProjectsState, UserSelectors.user, UserSelectors.userProjectAdminGroups, RouterSelectors.params])
@Selector([
ProjectsState,
ResourceSelectors.resource,
UserSelectors.user,
UserSelectors.userProjectAdminGroups,
ConfigState.getConfig,
RouterSelectors.params,
])
static isCurrentProjectAdminSysAdminOrMember(
state: ProjectsStateModel,
resource: DspResource,
user: ReadUser,
userProjectGroups: string[],
dspApiConfig: DspAppConfig,
params: Params
): boolean | undefined {
const projectIri = ProjectsSelectors.getProjectIri(params, dspApiConfig, resource);
const isMember = ProjectService.IsProjectMemberOrAdminOrSysAdmin(user, userProjectGroups, projectIri);
return isMember;
}

@Selector([
ProjectsState,
ResourceSelectors.resource,
UserSelectors.user,
UserSelectors.userProjectAdminGroups,
ConfigState.getConfig,
RouterSelectors.params,
])
static isCurrentProjectAdminOrSysAdmin(
state: ProjectsStateModel,
resource: DspResource,
user: ReadUser,
userProjectGroups: string[],
dspApiConfig: DspAppConfig,
params: Params
): boolean | undefined {
const projectId = params[`${RouteConstants.uuidParameter}`]
? params[`${RouteConstants.uuidParameter}`]
: params[`${RouteConstants.projectParameter}`];
const isProjectAdmin = ProjectService.IsProjectAdminOrSysAdmin(user, userProjectGroups, projectId);
const projectIri = ProjectsSelectors.getProjectIri(params, dspApiConfig, resource);
const isProjectAdmin = ProjectService.IsProjectAdminOrSysAdmin(user, userProjectGroups, projectIri);
return isProjectAdmin;
}

@Selector([ProjectsState, UserSelectors.user, UserSelectors.userProjectAdminGroups, RouterSelectors.params])
@Selector([
ProjectsState,
ResourceSelectors.resource,
UserSelectors.user,
UserSelectors.userProjectAdminGroups,
ConfigState.getConfig,
RouterSelectors.params,
])
static isCurrentProjectMember(
state: ProjectsStateModel,
resource: DspResource,
user: ReadUser,
userProjectGroups: string[],
dspApiConfig: DspAppConfig,
params: Params
): boolean | undefined {
const projectId = params[`${RouteConstants.uuidParameter}`];
const isProjectMember = ProjectService.IsProjectMember(user, userProjectGroups, projectId);
const projectIri = ProjectsSelectors.getProjectIri(params, dspApiConfig, resource);
const isProjectMember = ProjectService.IsProjectMember(user, userProjectGroups, projectIri);
return isProjectMember;
}

@Selector([ProjectsState, RouterSelectors.params])
@Selector([ProjectsState, ResourceSelectors.resource, ConfigState.getConfig, RouterSelectors.params])
static projectRestrictedViewSettings(
state: ProjectsStateModel,
resource: DspResource,
dspApiConfig: DspAppConfig,
params: Params
): ProjectRestrictedViewSettings | RestrictedViewResponse | undefined {
const projectUuid = params[`${RouteConstants.uuidParameter}`];
return state.projectRestrictedViewSettings[projectUuid]
? state.projectRestrictedViewSettings[projectUuid].value
const projectIri = ProjectsSelectors.getProjectIri(params, dspApiConfig, resource);
return state.projectRestrictedViewSettings[projectIri]
? state.projectRestrictedViewSettings[projectIri].value
: undefined;
}

private static getProjectIri(params: Params, dspApiConfig: DspAppConfig, resource: DspResource) {
const projectIri = params[`${RouteConstants.uuidParameter}`]
? params[`${RouteConstants.uuidParameter}`]
: resource.res.attachedToProject;
return ProjectService.uuidToIri(projectIri, dspApiConfig);
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { DspResource } from '@dasch-swiss/vre/shared/app-common';

export class GetAttachedUserAction {
static readonly type = '[Resource] Get Attached User';

Expand All @@ -16,3 +18,9 @@ export class GetAttachedProjectAction {
public projectIri: string
) {}
}

export class SetCurrentResourceAction {
static readonly type = '[Resource] Set Current Resource';

constructor(public resource: DspResource) {}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ReadProject, ReadUser } from '@dasch-swiss/dsp-js';
import { DspResource } from '@dasch-swiss/vre/shared/app-common';
import { Selector } from '@ngxs/store';
import { IKeyValuePairs } from '../model-interfaces';
import { ResourceState } from './resource.state';
Expand All @@ -14,4 +15,9 @@ export class ResourceSelectors {
static attachedProjects(state: ReourceStateModel): IKeyValuePairs<ReadProject> {
return state.attachedProjects;
}

@Selector([ResourceState])
static resource(state: ReourceStateModel): DspResource | null {
return state.resource;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { ReadProject, ReadUser } from '@dasch-swiss/dsp-js';
import { DspResource } from '@dasch-swiss/vre/shared/app-common';
import { IKeyValuePairs } from '../model-interfaces';

export class ReourceStateModel {
isLoading = false;
attachedUsers: IKeyValuePairs<ReadUser> = {};
attachedProjects: IKeyValuePairs<ReadProject> = {};
resource: DspResource | null = null;
}
8 changes: 7 additions & 1 deletion libs/vre/shared/app-state/src/lib/resource/resource.state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import { Action, State, StateContext, Store } from '@ngxs/store';
import { map, take } from 'rxjs/operators';
import { ProjectsSelectors } from '../projects/projects.selectors';
import { UserSelectors } from '../user/user.selectors';
import { GetAttachedProjectAction, GetAttachedUserAction } from './resource.actions';
import { GetAttachedProjectAction, GetAttachedUserAction, SetCurrentResourceAction } from './resource.actions';
import { ReourceStateModel } from './resource.state-model';

const defaults = <ReourceStateModel>{
isLoading: false,
attachedProjects: {},
attachedUsers: {},
resource: null,
};

@State<ReourceStateModel>({
Expand Down Expand Up @@ -109,4 +110,9 @@ export class ResourceState {
})
);
}

@Action(SetCurrentResourceAction)
setCurrentOntologyAction(ctx: StateContext<ReourceStateModel>, { resource }: SetCurrentResourceAction) {
ctx.patchState({ resource });
}
}
31 changes: 27 additions & 4 deletions libs/vre/shared/app-state/src/store.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
import { NgModule } from '@angular/core';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { AppConfigService } from '@dasch-swiss/vre/shared/app-config';
import { NgxsReduxDevtoolsPluginModule } from '@ngxs/devtools-plugin';
import { NgxsLoggerPluginModule } from '@ngxs/logger-plugin';
import { NgxsRouterPluginModule, RouterStateSerializer } from '@ngxs/router-plugin';
import { NgxsModule } from '@ngxs/store';
import { NgxsModule, Store } from '@ngxs/store';
import { CurrentPageState } from './lib/current-page/current-page.state';
import { CustomRouterStateSerializer } from './lib/router/router-state.serializer';
import { DEVTOOLS_REDUX_CONFIG, LOGGER_CONFIG, OPTIONS_CONFIG } from './store.config';
import { ListsState, OntologiesState, OntologyClassState, ProjectsState, ResourceState, UserState } from './index';
import {
ListsState,
OntologiesState,
OntologyClassState,
ProjectsState,
ResourceState,
UserState,
ConfigState,
SetConfigAction,
} from './index';

export function initializeConfigState(configService: AppConfigService, store: Store) {
return () => store.dispatch(new SetConfigAction(configService));
}

const STATE_MODULES = [
UserState,
Expand All @@ -16,6 +30,7 @@ const STATE_MODULES = [
ListsState,
OntologyClassState,
ResourceState,
ConfigState,
];

@NgModule({
Expand All @@ -26,6 +41,14 @@ const STATE_MODULES = [
NgxsLoggerPluginModule.forRoot(LOGGER_CONFIG),
NgxsRouterPluginModule.forRoot(),
],
providers: [{ provide: RouterStateSerializer, useClass: CustomRouterStateSerializer }],
providers: [
{ provide: RouterStateSerializer, useClass: CustomRouterStateSerializer },
{
provide: APP_INITIALIZER,
useFactory: initializeConfigState,
deps: [AppConfigService, Store],
multi: true,
},
],
})
export class NgxsStoreModule {}

0 comments on commit a04bb41

Please sign in to comment.