Skip to content

Commit

Permalink
Merge pull request #2884 from SeedCompany/0649-add-field-countries-to…
Browse files Browse the repository at this point in the history
…-partner
  • Loading branch information
CarsonF authored Oct 10, 2023
2 parents a834cf5 + 590f62c commit 90647c3
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 1 deletion.
6 changes: 6 additions & 0 deletions src/components/location/dto/location.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Secured,
SecuredEnum,
SecuredProperty,
SecuredPropertyList,
SecuredProps,
SecuredString,
SecuredStringNullable,
Expand Down Expand Up @@ -54,6 +55,11 @@ export class Location extends Resource {
})
export class SecuredLocation extends SecuredProperty(Location) {}

@ObjectType({
description: SecuredPropertyList.descriptionFor('a list of locations'),
})
export class SecuredLocations extends SecuredPropertyList(Location) {}

declare module '~/core/resources/map' {
interface ResourceMap {
Location: typeof Location;
Expand Down
6 changes: 6 additions & 0 deletions src/components/partner/dto/create-partner.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Transform, Type } from 'class-transformer';
import { Matches, ValidateNested } from 'class-validator';
import { uniq } from 'lodash';
import { ID, IdField, IdOf, IsId, NameField } from '../../../common';
import { Location } from '../../../components/location';
import { FieldRegion } from '../../field-region';
import type { Language } from '../../language';
import { FinancialReportingType } from '../../partnership/dto/financial-reporting-type';
Expand Down Expand Up @@ -43,6 +44,11 @@ export abstract class CreatePartner {
@IdField({ nullable: true })
readonly languageOfWiderCommunicationId?: IdOf<Language> | null;

@Field(() => [IDType], { nullable: true })
@IsId({ each: true })
@Transform(({ value }) => uniq(value))
readonly countries?: ReadonlyArray<IdOf<Location>> = [];

@Field(() => [IDType], { nullable: true })
@IsId({ each: true })
@Transform(({ value }) => uniq(value))
Expand Down
2 changes: 2 additions & 0 deletions src/components/partner/dto/partner.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
Sensitivity,
SensitivityField,
} from '../../../common';
import { Location } from '../../../components/location';
import { ScopedRole } from '../../authorization';
import { FieldRegion } from '../../field-region';
import type { Language } from '../../language';
Expand Down Expand Up @@ -78,6 +79,7 @@ export class Partner extends Interfaces {
readonly languageOfWiderCommunication: Secured<IdOf<Language> | null>;

readonly fieldRegions: Required<Secured<ReadonlyArray<IdOf<FieldRegion>>>>;
readonly countries: Required<Secured<ReadonlyArray<IdOf<Location>>>>;

@DateTimeField()
readonly modifiedAt: DateTime;
Expand Down
6 changes: 6 additions & 0 deletions src/components/partner/dto/update-partner.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Transform, Type } from 'class-transformer';
import { Matches, ValidateNested } from 'class-validator';
import { uniq } from 'lodash';
import { ID, IdField, IdOf, IsId, NameField } from '../../../common';
import { Location } from '../../../components/location';
import { FieldRegion } from '../../field-region';
import type { Language } from '../../language';
import { FinancialReportingType } from '../../partnership/dto/financial-reporting-type';
Expand Down Expand Up @@ -43,6 +44,11 @@ export abstract class UpdatePartner {
@IdField({ nullable: true })
readonly languageOfWiderCommunicationId?: IdOf<Language> | null;

@Field(() => [IDType], { nullable: true })
@IsId({ each: true })
@Transform(({ value }) => (value ? uniq(value) : undefined))
readonly countries?: ReadonlyArray<IdOf<Location>>;

@Field(() => [IDType], { nullable: true })
@IsId({ each: true })
@Transform(({ value }) => (value ? uniq(value) : undefined))
Expand Down
11 changes: 11 additions & 0 deletions src/components/partner/partner.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export class PartnerRepository extends DtoRepository<
input.languageOfWiderCommunicationId,
],
fieldRegions: ['FieldRegion', input.fieldRegions],
countries: ['Location', input.countries],
}),
)
.return<{ id: ID }>('node.id as id')
Expand Down Expand Up @@ -114,6 +115,15 @@ export class PartnerRepository extends DtoRepository<
])
.return(collect('fieldRegions.id').as('fieldRegionsIds')),
)
.subQuery('node', (sub) =>
sub
.match([
node('node'),
relation('out', '', 'countries'),
node('countries', 'Location'),
])
.return(collect('countries.id').as('countriesIds')),
)
.apply(matchProps())
.optionalMatch([
node('node'),
Expand All @@ -137,6 +147,7 @@ export class PartnerRepository extends DtoRepository<
pointOfContact: 'pointOfContact.id',
languageOfWiderCommunication: 'languageOfWiderCommunication.id',
fieldRegions: 'fieldRegionsIds',
countries: 'countriesIds',
scope: 'scopedRoles',
pinned: 'exists((:User { id: $requestingUser })-[:pinned]->(node))',
}).as('dto'),
Expand Down
9 changes: 9 additions & 0 deletions src/components/partner/partner.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import { Loader, LoaderOf } from '../../core';
import { FieldRegionLoader, SecuredFieldRegions } from '../field-region';
import { LanguageLoader, SecuredLanguageNullable } from '../language';
import { LocationLoader, SecuredLocations } from '../location';
import { OrganizationLoader, SecuredOrganization } from '../organization';
import { PartnerLoader, PartnerService } from '../partner';
import {
Expand Down Expand Up @@ -103,6 +104,14 @@ export class PartnerResolver {
return await loadSecuredIds(loader, partner.fieldRegions);
}

@ResolveField(() => SecuredLocations)
async countries(
@Parent() partner: Partner,
@Loader(LocationLoader) loader: LoaderOf<LocationLoader>,
): Promise<SecuredLocations> {
return await loadSecuredIds(loader, partner.countries);
}

@ResolveField(() => SecuredProjectList, {
description: 'The list of projects the partner has a partnership with.',
})
Expand Down
49 changes: 48 additions & 1 deletion src/components/partner/partner.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common';
import {
DuplicateException,
ID,
IdOf,
InputException,
loadManyIgnoreMissingThrowAny,
NotFoundException,
ObjectView,
ServerException,
Session,
UnauthorizedException,
UnsecuredDto,
} from '../../common';
import { HandleIdLookup, ILogger, Logger } from '../../core';
import { HandleIdLookup, ILogger, Logger, ResourceLoader } from '../../core';
import { mapListResults } from '../../core/database/results';
import { Privileges } from '../authorization';
import { Location, LocationLoader, LocationType } from '../location';
import { FinancialReportingType } from '../partnership/dto/financial-reporting-type';
import {
IProject,
Expand All @@ -38,6 +41,7 @@ export class PartnerService {
@Inject(forwardRef(() => ProjectService))
private readonly projectService: ProjectService & {},
private readonly repo: PartnerRepository,
private readonly resourceLoader: ResourceLoader,
) {}

async create(input: CreatePartner, session: Session): Promise<Partner> {
Expand All @@ -55,6 +59,10 @@ export class PartnerService {
);
}

if (input.countries) {
await this.verifyCountries(input.countries);
}

const id = await this.repo.create(input, session);

this.logger.debug(`Partner created`, { id });
Expand Down Expand Up @@ -125,6 +133,7 @@ export class PartnerService {
pointOfContactId,
languageOfWiderCommunicationId,
fieldRegions,
countries,
...simpleChanges
} = changes;

Expand All @@ -148,6 +157,22 @@ export class PartnerService {
);
}

if (countries) {
await this.verifyCountries(countries);

try {
await this.repo.updateRelationList({
id: partner.id,
relation: 'countries',
newList: countries,
});
} catch (e) {
throw e instanceof InputException
? e.withField('partner.countries')
: e;
}
}

if (fieldRegions) {
try {
await this.repo.updateRelationList({
Expand Down Expand Up @@ -234,4 +259,26 @@ export class PartnerService {
? false
: true;
}

private async verifyCountries(ids: ReadonlyArray<IdOf<Location>>) {
const loader = await this.resourceLoader.getLoader(LocationLoader);
const locations = await loadManyIgnoreMissingThrowAny(loader, ids);
const invalidIds = locations.flatMap((location) =>
location.type.value !== 'Country' ? location.id : [],
);
if (invalidIds.length === 0) {
return;
}
const ex = new LocationTypeException([LocationType.Country], invalidIds);
throw ex.withField('partner.countries');
}
}

class LocationTypeException extends InputException {
constructor(
readonly allowedTypes: readonly LocationType[],
readonly invalidIds: ID[],
) {
super('Given locations do not match the expected type');
}
}

0 comments on commit 90647c3

Please sign in to comment.