-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(api): projects: make place for acl/resource logic before crud
- Loading branch information
Showing
7 changed files
with
313 additions
and
238 deletions.
There are no files selected for viewing
16 changes: 16 additions & 0 deletions
16
api/apps/api/src/modules/projects/dto/geo-feature.mapper.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { GeoFeaturesService } from '../../geo-features/geo-features.service'; | ||
import { PaginationMeta } from '../../../utils/app-base.service'; | ||
import { GeoFeature } from '../../geo-features/geo-feature.api.entity'; | ||
|
||
@Injectable() | ||
export class GeoFeatureMapper { | ||
constructor(private readonly geoFeaturesService: GeoFeaturesService) {} | ||
|
||
async serialize( | ||
entities: Partial<GeoFeature> | (Partial<GeoFeature> | undefined)[], | ||
paginationMeta?: PaginationMeta, | ||
): Promise<any> { | ||
return this.geoFeaturesService.serialize(entities, paginationMeta); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { PaginationMeta } from '../../../utils/app-base.service'; | ||
import { ProjectsCrud } from '../projects-crud'; | ||
import { Project } from '../project.api.entity'; | ||
|
||
@Injectable() | ||
export class ProjectMapper { | ||
constructor(private readonly projectsCrud: ProjectsCrud) {} | ||
|
||
async serialize( | ||
entities: Partial<Project> | (Partial<Project> | undefined)[], | ||
paginationMeta?: PaginationMeta, | ||
): Promise<any> { | ||
return this.projectsCrud.serialize(entities, paginationMeta); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
import { forwardRef, Inject, Injectable } from '@nestjs/common'; | ||
import { InjectRepository } from '@nestjs/typeorm'; | ||
import { AppInfoDTO } from 'dto/info.dto'; | ||
import { Repository, SelectQueryBuilder } from 'typeorm'; | ||
import { Project } from './project.api.entity'; | ||
import { CreateProjectDTO } from './dto/create.project.dto'; | ||
import { UpdateProjectDTO } from './dto/update.project.dto'; | ||
import { UsersService } from 'modules/users/users.service'; | ||
import { ScenariosService } from 'modules/scenarios/scenarios.service'; | ||
import { PlanningUnitsService } from 'modules/planning-units/planning-units.service'; | ||
import { | ||
AppBaseService, | ||
JSONAPISerializerConfig, | ||
} from 'utils/app-base.service'; | ||
import { Country } from 'modules/countries/country.geo.entity'; | ||
import { AdminArea } from 'modules/admin-areas/admin-area.geo.entity'; | ||
import { AdminAreasService } from 'modules/admin-areas/admin-areas.service'; | ||
import { CountriesService } from 'modules/countries/countries.service'; | ||
import { AppConfig } from 'utils/config.utils'; | ||
|
||
const projectFilterKeyNames = [ | ||
'name', | ||
'organizationId', | ||
'countryId', | ||
'adminAreaLevel1Id', | ||
'adminAreaLevel2Id', | ||
] as const; | ||
type ProjectFilterKeys = keyof Pick< | ||
Project, | ||
typeof projectFilterKeyNames[number] | ||
>; | ||
type ProjectFilters = Record<ProjectFilterKeys, string[]>; | ||
|
||
@Injectable() | ||
export class ProjectsCrud extends AppBaseService< | ||
Project, | ||
CreateProjectDTO, | ||
UpdateProjectDTO, | ||
AppInfoDTO | ||
> { | ||
constructor( | ||
@InjectRepository(Project) | ||
protected readonly repository: Repository<Project>, | ||
@Inject(forwardRef(() => ScenariosService)) | ||
protected readonly scenariosService: ScenariosService, | ||
@Inject(UsersService) protected readonly usersService: UsersService, | ||
@Inject(AdminAreasService) | ||
protected readonly adminAreasService: AdminAreasService, | ||
@Inject(CountriesService) | ||
protected readonly countriesService: CountriesService, | ||
@Inject(PlanningUnitsService) | ||
private readonly planningUnitsService: PlanningUnitsService, | ||
) { | ||
super(repository, 'project', 'projects', { | ||
logging: { muteAll: AppConfig.get<boolean>('logging.muteAll', false) }, | ||
}); | ||
} | ||
|
||
get serializerConfig(): JSONAPISerializerConfig<Project> { | ||
return { | ||
attributes: [ | ||
'name', | ||
'description', | ||
'countryId', | ||
'adminAreaLevel1Id', | ||
'adminAreaLevel2Id', | ||
'planningUnitGridShape', | ||
'planningUnitAreakm2', | ||
'users', | ||
'scenarios', | ||
'createdAt', | ||
'lastModifiedAt', | ||
], | ||
keyForAttribute: 'camelCase', | ||
users: { | ||
ref: 'id', | ||
attributes: ['fname', 'lname', 'email', 'projectRoles'], | ||
projectRoles: { | ||
ref: 'name', | ||
attributes: ['name'], | ||
}, | ||
}, | ||
scenarios: { | ||
ref: 'id', | ||
attributes: [ | ||
'name', | ||
'description', | ||
'type', | ||
'wdpaFilter', | ||
'wdpaThreshold', | ||
'adminRegionId', | ||
'numberOfRuns', | ||
'boundaryLengthModifier', | ||
'metadata', | ||
'status', | ||
'createdAt', | ||
'lastModifiedAt', | ||
], | ||
}, | ||
}; | ||
} | ||
|
||
/** | ||
* Apply service-specific filters. | ||
*/ | ||
setFilters( | ||
query: SelectQueryBuilder<Project>, | ||
filters: ProjectFilters, | ||
_info?: AppInfoDTO, | ||
): SelectQueryBuilder<Project> { | ||
this._processBaseFilters<ProjectFilters>( | ||
query, | ||
filters, | ||
projectFilterKeyNames, | ||
); | ||
return query; | ||
} | ||
|
||
async setDataCreate( | ||
create: CreateProjectDTO, | ||
info?: AppInfoDTO, | ||
): Promise<Project> { | ||
/** | ||
* @debt Temporary setup. I think we should remove TimeUserEntityMetadata | ||
* from entities and just use a separate event log, and a view to obtain the | ||
* same information (who created an entity and when, and when it was last | ||
* modified) from that log, kind of event sourcing way. | ||
*/ | ||
const project = await super.setDataCreate(create, info); | ||
project.createdBy = info?.authenticatedUser?.id!; | ||
return project; | ||
} | ||
|
||
/** | ||
* Look up the planning area for this project. | ||
* | ||
* In decreasing precedence (i.e. most specific is used): | ||
* | ||
* * a project-specific protected area (@todo not implemented yet) | ||
* * a level 2 admin area | ||
* * a level 1 admin area | ||
* * a country | ||
*/ | ||
async getPlanningArea( | ||
project: Partial<Project>, | ||
): Promise<Country | Partial<AdminArea | undefined>> { | ||
const planningArea = project.planningAreaGeometryId | ||
? /** | ||
* @todo here we should look up the actual custom planning area from | ||
* `planningAreaGeometryId`, when we implement this. | ||
*/ | ||
new AdminArea() | ||
: project.adminAreaLevel2Id | ||
? await this.adminAreasService.getByLevel1OrLevel2Id( | ||
project.adminAreaLevel2Id!, | ||
) | ||
: project.adminAreaLevel1Id | ||
? await this.adminAreasService.getByLevel1OrLevel2Id( | ||
project.adminAreaLevel1Id!, | ||
) | ||
: project.countryId | ||
? await this.countriesService.getById(project.countryId) | ||
: undefined; | ||
return planningArea; | ||
} | ||
|
||
async actionAfterCreate( | ||
model: Project, | ||
createModel: CreateProjectDTO, | ||
_info?: AppInfoDTO, | ||
): Promise<void> { | ||
if ( | ||
createModel.planningUnitAreakm2 && | ||
createModel.planningUnitGridShape && | ||
(createModel.countryId || | ||
createModel.adminAreaLevel1Id || | ||
createModel.adminAreaLevel2Id || | ||
createModel.extent) | ||
) { | ||
this.logger.debug('creating planning unit job '); | ||
return this.planningUnitsService.create(createModel); | ||
} | ||
} | ||
|
||
async actionAfterUpdate( | ||
model: Project, | ||
createModel: UpdateProjectDTO, | ||
_info?: AppInfoDTO, | ||
): Promise<void> { | ||
if ( | ||
createModel.planningUnitAreakm2 && | ||
createModel.planningUnitGridShape && | ||
(createModel.countryId || | ||
createModel.adminAreaLevel1Id || | ||
createModel.adminAreaLevel2Id || | ||
createModel.extent) | ||
) { | ||
this.logger.debug('creating planning unit job '); | ||
return this.planningUnitsService.create(createModel); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.