From 4a1d7312b0cd82f39744b0d120c79aded50f6a1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnea=20R=C3=BAn=20Vignisd=C3=B3ttir?= Date: Tue, 15 Oct 2024 15:33:11 +0000 Subject: [PATCH 01/11] fix(service-portal): Delete and view delegation in service portal form me list (#16400) * fixes wrong validTo date in DelegationViewModal and show delete button for outgoing delegation * remove log * chore: nx format:write update dirty files --------- Co-authored-by: andes-it --- .../components/delegations/DelegationViewModal.tsx | 2 +- .../delegations/outgoing/DelegationsOutgoing.tsx | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/libs/portals/shared-modules/delegations/src/components/delegations/DelegationViewModal.tsx b/libs/portals/shared-modules/delegations/src/components/delegations/DelegationViewModal.tsx index b06a5e7c0d5f..277823485167 100644 --- a/libs/portals/shared-modules/delegations/src/components/delegations/DelegationViewModal.tsx +++ b/libs/portals/shared-modules/delegations/src/components/delegations/DelegationViewModal.tsx @@ -154,7 +154,7 @@ export const DelegationViewModal = ({ { { - setDelegation( - delegation as AuthCustomDelegationOutgoing, - ) - } - : undefined - } + onDelete={(delegation) => { + setDelegation( + delegation as AuthCustomDelegationOutgoing, + ) + }} onEdit={ !isGeneralMandate ? (delegation) => From f560cd296e9153dcbbe50d0dbd1ee64fcc0d652a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:09:50 +0000 Subject: [PATCH 02/11] feat(web): Generic list - External url field (#16348) * Add next external url field * Remove placeholder --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/web/components/GenericList/GenericList.tsx | 11 +++++++++-- apps/web/screens/queries/GenericList.ts | 1 + libs/cms/src/lib/generated/contentfulTypes.d.ts | 3 +++ libs/cms/src/lib/models/genericListItem.model.ts | 4 ++++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/apps/web/components/GenericList/GenericList.tsx b/apps/web/components/GenericList/GenericList.tsx index 0c6153dbaabb..d717d1616388 100644 --- a/apps/web/components/GenericList/GenericList.tsx +++ b/apps/web/components/GenericList/GenericList.tsx @@ -16,6 +16,7 @@ import { GridContainer, GridRow, Icon, + type IconProps, Inline, Pagination, Stack, @@ -126,9 +127,15 @@ export const ClickableItem = ({ item, baseUrl }: ClickableItemProps) => { const pathname = new URL(baseUrl || router.asPath, 'https://island.is') .pathname + let icon: IconProps['icon'] | null = null + let href = item.slug ? `${pathname}/${item.slug}` : undefined if (item.assetUrl) { href = item.assetUrl + icon = 'document' + } else if (item.externalUrl) { + href = item.externalUrl + icon = 'open' } const filterTags = item.filterTags ?? [] @@ -151,12 +158,12 @@ export const ClickableItem = ({ item, baseUrl }: ClickableItemProps) => { {item.date && format(new Date(item.date), 'dd.MM.yyyy')} - {item.assetUrl && ( + {icon && ( )} diff --git a/apps/web/screens/queries/GenericList.ts b/apps/web/screens/queries/GenericList.ts index 1d4e0fb15f68..5bfab44d76bf 100644 --- a/apps/web/screens/queries/GenericList.ts +++ b/apps/web/screens/queries/GenericList.ts @@ -24,6 +24,7 @@ export const GET_GENERIC_LIST_ITEMS_QUERY = gql` } slug assetUrl + externalUrl } total } diff --git a/libs/cms/src/lib/generated/contentfulTypes.d.ts b/libs/cms/src/lib/generated/contentfulTypes.d.ts index 62503dab4ec1..9301bded9a0d 100644 --- a/libs/cms/src/lib/generated/contentfulTypes.d.ts +++ b/libs/cms/src/lib/generated/contentfulTypes.d.ts @@ -1606,6 +1606,9 @@ export interface IGenericListItemFields { /** Asset */ asset?: Asset | undefined + + /** External Link */ + externalLink?: ILinkUrl | undefined } /** An item that belongs to a generic list */ diff --git a/libs/cms/src/lib/models/genericListItem.model.ts b/libs/cms/src/lib/models/genericListItem.model.ts index a8ee4ce0e825..39fe5dc37b6e 100644 --- a/libs/cms/src/lib/models/genericListItem.model.ts +++ b/libs/cms/src/lib/models/genericListItem.model.ts @@ -31,6 +31,9 @@ export class GenericListItem { @Field(() => String, { nullable: true }) assetUrl?: string + @Field(() => String, { nullable: true }) + externalUrl?: string + @CacheField(() => [GenericTag], { nullable: true }) filterTags?: GenericTag[] } @@ -62,6 +65,7 @@ export const mapGenericListItem = ({ : [], slug: fields.slug, assetUrl, + externalUrl: fields.externalLink?.fields?.url ?? '', filterTags: fields.filterTags ? fields.filterTags.map(mapGenericTag) : [], } } From 4a0a50abace57fce376b41431fe4dd23bbbbc98b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:45:29 +0000 Subject: [PATCH 03/11] feat(search-indexer): Move mapping logic to separate class (#16410) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- libs/cms/src/lib/search/cmsSync.module.ts | 2 + libs/cms/src/lib/search/cmsSync.service.ts | 87 +---------------- libs/cms/src/lib/search/contentful.service.ts | 31 ++++++- libs/cms/src/lib/search/mapping.service.ts | 93 +++++++++++++++++++ 4 files changed, 128 insertions(+), 85 deletions(-) create mode 100644 libs/cms/src/lib/search/mapping.service.ts diff --git a/libs/cms/src/lib/search/cmsSync.module.ts b/libs/cms/src/lib/search/cmsSync.module.ts index 261082084628..d6edead06251 100644 --- a/libs/cms/src/lib/search/cmsSync.module.ts +++ b/libs/cms/src/lib/search/cmsSync.module.ts @@ -32,6 +32,7 @@ import { ManualChapterItemSyncService } from './importers/manualChapterItem.serv import { CustomPageSyncService } from './importers/customPage.service' import { GenericListItemSyncService } from './importers/genericListItem.service' import { TeamListSyncService } from './importers/teamList.service' +import { MappingService } from './mapping.service' @Module({ imports: [ @@ -69,6 +70,7 @@ import { TeamListSyncService } from './importers/teamList.service' CustomPageSyncService, GenericListItemSyncService, TeamListSyncService, + MappingService, ], exports: [CmsSyncService], }) diff --git a/libs/cms/src/lib/search/cmsSync.service.ts b/libs/cms/src/lib/search/cmsSync.service.ts index b1593bf2c158..f11d3151192a 100644 --- a/libs/cms/src/lib/search/cmsSync.service.ts +++ b/libs/cms/src/lib/search/cmsSync.service.ts @@ -8,34 +8,11 @@ import { SyncOptions, SyncResponse, } from '@island.is/content-search-indexer/types' -import { ArticleSyncService } from './importers/article.service' -import { SubArticleSyncService } from './importers/subArticle.service' import { ContentfulService } from './contentful.service' -import { AnchorPageSyncService } from './importers/anchorPage.service' -import { LifeEventPageSyncService } from './importers/lifeEventPage.service' -import { ArticleCategorySyncService } from './importers/articleCategory.service' -import { NewsSyncService } from './importers/news.service' import { Entry } from 'contentful' import { ElasticService, SearchInput } from '@island.is/content-search-toolkit' -import { AdgerdirPageSyncService } from './importers/adgerdirPage' -import { MenuSyncService } from './importers/menu.service' -import { GroupedMenuSyncService } from './importers/groupedMenu.service' import { getElasticsearchIndex } from '@island.is/content-search-index-manager' -import { OrganizationPageSyncService } from './importers/organizationPage.service' -import { OrganizationSubpageSyncService } from './importers/organizationSubpage.service' -import { FrontpageSyncService } from './importers/frontpage.service' -import { SupportQNASyncService } from './importers/supportQNA.service' -import { LinkSyncService } from './importers/link.service' -import { ProjectPageSyncService } from './importers/projectPage.service' -import { EnhancedAssetSyncService } from './importers/enhancedAsset.service' -import { VacancySyncService } from './importers/vacancy.service' -import { ServiceWebPageSyncService } from './importers/serviceWebPage.service' -import { EventSyncService } from './importers/event.service' -import { ManualSyncService } from './importers/manual.service' -import { ManualChapterItemSyncService } from './importers/manualChapterItem.service' -import { CustomPageSyncService } from './importers/customPage.service' -import { GenericListItemSyncService } from './importers/genericListItem.service' -import { TeamListSyncService } from './importers/teamList.service' +import { MappingService } from './mapping.service' export interface PostSyncOptions { folderHash: string @@ -56,62 +33,11 @@ export interface CmsSyncProvider { @Injectable() export class CmsSyncService implements ContentSearchImporter { - private contentSyncProviders: CmsSyncProvider[] constructor( - private readonly newsSyncService: NewsSyncService, - private readonly articleCategorySyncService: ArticleCategorySyncService, - private readonly articleSyncService: ArticleSyncService, - private readonly subArticleSyncService: SubArticleSyncService, - private readonly anchorPageSyncService: AnchorPageSyncService, - private readonly lifeEventPageSyncService: LifeEventPageSyncService, - private readonly adgerdirPageSyncService: AdgerdirPageSyncService, private readonly contentfulService: ContentfulService, - private readonly menuSyncService: MenuSyncService, - private readonly groupedMenuSyncService: GroupedMenuSyncService, - private readonly organizationPageSyncService: OrganizationPageSyncService, - private readonly organizationSubpageSyncService: OrganizationSubpageSyncService, - private readonly projectPageSyncService: ProjectPageSyncService, - private readonly frontpageSyncService: FrontpageSyncService, - private readonly supportQNASyncService: SupportQNASyncService, - private readonly linkSyncService: LinkSyncService, - private readonly enhancedAssetService: EnhancedAssetSyncService, + private readonly mappingService: MappingService, private readonly elasticService: ElasticService, - private readonly vacancyService: VacancySyncService, - private readonly serviceWebPageSyncService: ServiceWebPageSyncService, - private readonly eventSyncService: EventSyncService, - private readonly manualSyncService: ManualSyncService, - private readonly manualChapterItemSyncService: ManualChapterItemSyncService, - private readonly customPageSyncService: CustomPageSyncService, - private readonly genericListItemSyncService: GenericListItemSyncService, - private readonly teamListSyncService: TeamListSyncService, - ) { - this.contentSyncProviders = [ - this.articleSyncService, - this.subArticleSyncService, - this.anchorPageSyncService, - this.lifeEventPageSyncService, - this.articleCategorySyncService, - this.newsSyncService, - this.adgerdirPageSyncService, - this.menuSyncService, - this.groupedMenuSyncService, - this.organizationPageSyncService, - this.organizationSubpageSyncService, - this.projectPageSyncService, - this.frontpageSyncService, - this.supportQNASyncService, - this.linkSyncService, - this.enhancedAssetService, - this.vacancyService, - this.serviceWebPageSyncService, - this.eventSyncService, - this.manualSyncService, - this.manualChapterItemSyncService, - this.customPageSyncService, - this.genericListItemSyncService, - this.teamListSyncService, - ] - } + ) {} private async getLastFolderHash(elasticIndex: string): Promise { logger.info('Getting folder hash from index', { @@ -228,12 +154,7 @@ export class CmsSyncService implements ContentSearchImporter { logger.info('Got sync data') // import data from all providers - const importableData = this.contentSyncProviders.map( - (contentSyncProvider) => { - const data = contentSyncProvider.processSyncData(items) - return contentSyncProvider.doMapping(data) - }, - ) + const importableData = this.mappingService.mapData(items) return { add: flatten(importableData), diff --git a/libs/cms/src/lib/search/contentful.service.ts b/libs/cms/src/lib/search/contentful.service.ts index 5d015de20bd9..878b0ca37a05 100644 --- a/libs/cms/src/lib/search/contentful.service.ts +++ b/libs/cms/src/lib/search/contentful.service.ts @@ -24,6 +24,7 @@ import { Locale } from '@island.is/shared/types' import type { ApiResponse } from '@elastic/elasticsearch' import type { SearchResponse } from '@island.is/shared/types' import type { MappedData } from '@island.is/content-search-indexer/types' +import { MappingService } from './mapping.service' type SyncCollection = ContentfulSyncCollection & { nextPageToken?: string @@ -79,6 +80,7 @@ export class ContentfulService { constructor( private readonly elasticService: ElasticService, private readonly featureFlagService: FeatureFlagService, + private readonly mappingService: MappingService, ) { const params: CreateClientParams = { space: environment.contentful.space, @@ -477,6 +479,9 @@ export class ContentfulService { const idsCopy = [...ids] let idsChunk = idsCopy.splice(-MAX_REQUEST_COUNT, MAX_REQUEST_COUNT) + let nestedProgress = idsChunk.length + const totalNested = ids.length + while (idsChunk.length > 0) { const size = 100 let page = 1 @@ -566,7 +571,11 @@ export class ContentfulService { (id) => !indexableEntries.some((entry) => entry.sys.id === id), ) + let progress = 0 + const total = rootEntryIds.length + let chunkIds = rootEntryIds.splice(-chunkSize, chunkSize) + progress += chunkIds.length while (chunkIds.length > 0) { const items = await this.getContentfulData(chunkSize, { @@ -574,12 +583,30 @@ export class ContentfulService { 'sys.id[in]': chunkIds.join(','), locale: this.contentfulLocaleMap[locale], }) - indexableEntries.push(...items) + + // import data from all providers + const importableData = this.mappingService.mapData(items) + + await this.elasticService.bulk(elasticIndex, { + add: flatten(importableData), + remove: [], + }) + + logger.info( + `${progress}/${total} resolved root entries have been synced`, + ) + chunkIds = rootEntryIds.splice(-chunkSize, chunkSize) + progress += chunkIds.length } } + logger.info( + `${nestedProgress}/${totalNested} nested entries have been resolved`, + ) + idsChunk = idsCopy.splice(-MAX_REQUEST_COUNT, MAX_REQUEST_COUNT) + nestedProgress += idsChunk.length } } @@ -643,7 +670,7 @@ export class ContentfulService { elasticIndex, locale, chunkSize, - indexableEntries, // This array is modified + indexableEntries, ) logger.info( diff --git a/libs/cms/src/lib/search/mapping.service.ts b/libs/cms/src/lib/search/mapping.service.ts new file mode 100644 index 000000000000..981675af8792 --- /dev/null +++ b/libs/cms/src/lib/search/mapping.service.ts @@ -0,0 +1,93 @@ +import { Injectable } from '@nestjs/common' + +import { ArticleSyncService } from './importers/article.service' +import { SubArticleSyncService } from './importers/subArticle.service' +import { AnchorPageSyncService } from './importers/anchorPage.service' +import { LifeEventPageSyncService } from './importers/lifeEventPage.service' +import { ArticleCategorySyncService } from './importers/articleCategory.service' +import { NewsSyncService } from './importers/news.service' +import { AdgerdirPageSyncService } from './importers/adgerdirPage' +import { MenuSyncService } from './importers/menu.service' +import { GroupedMenuSyncService } from './importers/groupedMenu.service' +import { OrganizationPageSyncService } from './importers/organizationPage.service' +import { OrganizationSubpageSyncService } from './importers/organizationSubpage.service' +import { FrontpageSyncService } from './importers/frontpage.service' +import { SupportQNASyncService } from './importers/supportQNA.service' +import { LinkSyncService } from './importers/link.service' +import { ProjectPageSyncService } from './importers/projectPage.service' +import { EnhancedAssetSyncService } from './importers/enhancedAsset.service' +import { VacancySyncService } from './importers/vacancy.service' +import { ServiceWebPageSyncService } from './importers/serviceWebPage.service' +import { EventSyncService } from './importers/event.service' +import { ManualSyncService } from './importers/manual.service' +import { ManualChapterItemSyncService } from './importers/manualChapterItem.service' +import { CustomPageSyncService } from './importers/customPage.service' +import { GenericListItemSyncService } from './importers/genericListItem.service' +import { TeamListSyncService } from './importers/teamList.service' +import type { CmsSyncProvider, processSyncDataInput } from './cmsSync.service' + +@Injectable() +export class MappingService { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private contentSyncProviders: CmsSyncProvider[] + constructor( + private readonly newsSyncService: NewsSyncService, + private readonly articleCategorySyncService: ArticleCategorySyncService, + private readonly articleSyncService: ArticleSyncService, + private readonly subArticleSyncService: SubArticleSyncService, + private readonly anchorPageSyncService: AnchorPageSyncService, + private readonly lifeEventPageSyncService: LifeEventPageSyncService, + private readonly adgerdirPageSyncService: AdgerdirPageSyncService, + private readonly menuSyncService: MenuSyncService, + private readonly groupedMenuSyncService: GroupedMenuSyncService, + private readonly organizationPageSyncService: OrganizationPageSyncService, + private readonly organizationSubpageSyncService: OrganizationSubpageSyncService, + private readonly projectPageSyncService: ProjectPageSyncService, + private readonly frontpageSyncService: FrontpageSyncService, + private readonly supportQNASyncService: SupportQNASyncService, + private readonly linkSyncService: LinkSyncService, + private readonly enhancedAssetService: EnhancedAssetSyncService, + private readonly vacancyService: VacancySyncService, + private readonly serviceWebPageSyncService: ServiceWebPageSyncService, + private readonly eventSyncService: EventSyncService, + private readonly manualSyncService: ManualSyncService, + private readonly manualChapterItemSyncService: ManualChapterItemSyncService, + private readonly customPageSyncService: CustomPageSyncService, + private readonly genericListItemSyncService: GenericListItemSyncService, + private readonly teamListSyncService: TeamListSyncService, + ) { + this.contentSyncProviders = [ + this.articleSyncService, + this.subArticleSyncService, + this.anchorPageSyncService, + this.lifeEventPageSyncService, + this.articleCategorySyncService, + this.newsSyncService, + this.adgerdirPageSyncService, + this.menuSyncService, + this.groupedMenuSyncService, + this.organizationPageSyncService, + this.organizationSubpageSyncService, + this.projectPageSyncService, + this.frontpageSyncService, + this.supportQNASyncService, + this.linkSyncService, + this.enhancedAssetService, + this.vacancyService, + this.serviceWebPageSyncService, + this.eventSyncService, + this.manualSyncService, + this.manualChapterItemSyncService, + this.customPageSyncService, + this.genericListItemSyncService, + this.teamListSyncService, + ] + } + + mapData(entries: processSyncDataInput) { + return this.contentSyncProviders.map((contentSyncProvider) => { + const data = contentSyncProvider.processSyncData(entries) + return contentSyncProvider.doMapping(data) + }) + } +} From 420313dd427e7ad50f2ab8c3c7e03eff86f7d4df Mon Sep 17 00:00:00 2001 From: juni-haukur <158475136+juni-haukur@users.noreply.github.com> Date: Tue, 15 Oct 2024 17:21:42 +0000 Subject: [PATCH 04/11] fix(signature-collection): Fix paper signatures for candidacy (#16411) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../src/lib/signatureCollection.service.ts | 3 +- .../signature-collection-shared.service.ts | 4 +- .../src/lib/signature-collection.service.ts | 38 +++++++++++-------- .../ViewList/Signees/PaperSignees.tsx | 2 +- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/libs/api/domains/signature-collection/src/lib/signatureCollection.service.ts b/libs/api/domains/signature-collection/src/lib/signatureCollection.service.ts index b1c79f77a8dd..7ef0f10a9e4b 100644 --- a/libs/api/domains/signature-collection/src/lib/signatureCollection.service.ts +++ b/libs/api/domains/signature-collection/src/lib/signatureCollection.service.ts @@ -176,7 +176,8 @@ export class SignatureCollectionService { const canSign = signatureSignee.canSign || (signatureSignee.canSignInfo?.length === 1 && - signatureSignee.canSignInfo[0] === ReasonKey.AlreadySigned) + (signatureSignee.canSignInfo[0] === ReasonKey.AlreadySigned || + signatureSignee.canSignInfo[0] === ReasonKey.noInvalidSignature)) return canSign && list.area.id === signatureSignee.area?.id } diff --git a/libs/clients/signature-collection/src/lib/signature-collection-shared.service.ts b/libs/clients/signature-collection/src/lib/signature-collection-shared.service.ts index 9fc2186e757d..23db1a8c239f 100644 --- a/libs/clients/signature-collection/src/lib/signature-collection-shared.service.ts +++ b/libs/clients/signature-collection/src/lib/signature-collection-shared.service.ts @@ -90,9 +90,7 @@ export class SignatureCollectionSharedClientService { const signatures = await api.medmaelalistarIDMedmaeliGet({ iD: parseInt(listId), }) - return signatures - .map((signature) => mapSignature(signature)) - .filter((s) => s.valid) + return signatures.map((signature) => mapSignature(signature)) } canCreate({ diff --git a/libs/clients/signature-collection/src/lib/signature-collection.service.ts b/libs/clients/signature-collection/src/lib/signature-collection.service.ts index 6f463142142d..72f677989a88 100644 --- a/libs/clients/signature-collection/src/lib/signature-collection.service.ts +++ b/libs/clients/signature-collection/src/lib/signature-collection.service.ts @@ -277,23 +277,29 @@ export class SignatureCollectionClientService { pageNumber, }: { listId: string; nationalId: string; pageNumber: number }, ): Promise { - const newSignature = await this.getApiWithAuth( - this.listsApi, - auth, - ).medmaelalistarIDMedmaeliBulkPost({ - medmaeliBulkRequestDTO: { - medmaeli: [ - { - kennitala: nationalId, - bladsida: pageNumber, - }, - ], - }, - iD: parseInt(listId), - }) + try { + await this.getApiWithAuth( + this.listsApi, + auth, + ).medmaelalistarIDMedmaeliBulkPost({ + medmaeliBulkRequestDTO: { + medmaeli: [ + { + kennitala: nationalId, + bladsida: pageNumber, + }, + ], + }, + iD: parseInt(listId), + }) - return { - success: !!newSignature.medmaeliKenn?.includes(nationalId), + return { + success: true, + } + } catch { + return { + success: false, + } } } diff --git a/libs/service-portal/signature-collection/src/screens/Parliamentary/OwnerView/ViewList/Signees/PaperSignees.tsx b/libs/service-portal/signature-collection/src/screens/Parliamentary/OwnerView/ViewList/Signees/PaperSignees.tsx index 07669de0572b..b01e6e3d6ef2 100644 --- a/libs/service-portal/signature-collection/src/screens/Parliamentary/OwnerView/ViewList/Signees/PaperSignees.tsx +++ b/libs/service-portal/signature-collection/src/screens/Parliamentary/OwnerView/ViewList/Signees/PaperSignees.tsx @@ -73,7 +73,7 @@ export const PaperSignees = ({ }, }, onCompleted: (res) => { - if (res.success) { + if (res.signatureCollectionUploadPaperSignature?.success) { toast.success(formatMessage(m.paperSigneeSuccess)) refetchSignees() } else { From e3e51fc733a277d0ecb5f0abbb8334ffab89dbe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Svanhildur=20Einarsd=C3=B3ttir?= <54863023+svanaeinars@users.noreply.github.com> Date: Tue, 15 Oct 2024 17:31:59 +0000 Subject: [PATCH 05/11] chore(infra): Fix run-local-env services argument (#16399) * chore: Fix run-local-env services argument * Added comment for yargs services check validation --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- infra/src/cli/cli.ts | 56 ++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/infra/src/cli/cli.ts b/infra/src/cli/cli.ts index 78cf78f8d92a..9be060943dbd 100644 --- a/infra/src/cli/cli.ts +++ b/infra/src/cli/cli.ts @@ -85,31 +85,41 @@ const cli = yargs(process.argv.slice(2)) }, ) .command( - 'run-local-env', + 'run-local-env [services...]', 'Render environment and run the local environment.\nThis is to be used when developing locally and loading of the environment variables for "dev" environment is needed.', (yargs) => { - return yargs - .positional('services', { - type: 'string', - array: true, - demandOption: true, - }) - .option('dependencies', { array: true, type: 'string', default: [] }) - .option('json', { type: 'boolean', default: false }) - .option('dry', { type: 'boolean', default: false }) - .option('no-update-secrets', { - type: 'boolean', - default: false, - alias: ['nosecrets', 'no-secrets'], - }) - .option('print', { type: 'boolean', default: false }) - .option('proxies', { type: 'boolean', default: false }) - .option('never-fail', { - alias: 'nofail', - type: 'boolean', - default: false, - }) - .demandCommand(1, 'You must pass at least one service to run!') + return ( + yargs + .positional('services', { + type: 'string', + array: true, + demandOption: true, + }) + .option('dependencies', { array: true, type: 'string', default: [] }) + .option('json', { type: 'boolean', default: false }) + .option('dry', { type: 'boolean', default: false }) + .option('no-update-secrets', { + type: 'boolean', + default: false, + alias: ['nosecrets', 'no-secrets'], + }) + .option('print', { type: 'boolean', default: false }) + .option('proxies', { type: 'boolean', default: false }) + .option('never-fail', { + alias: 'nofail', + type: 'boolean', + default: false, + }) + // Custom check for 'services' since yargs lack built-in validation + .check((argv) => { + const svc = argv.services + if (svc.length < 1) { + throw new Error('You must pass at least one service to run!') + } else { + return true + } + }) + ) }, async (argv) => await runLocalServices(argv.services, argv.dependencies, { From 7a37f44478d35f6674028e5027d90e1a650ce8dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Tue, 15 Oct 2024 21:38:57 +0000 Subject: [PATCH 06/11] chore(j-s): Fixes to ServiceRequirement NOT_REQUIRED (#16305) * Prevent p inside p warning * Remove the check for SERVICE_REQUIREMENT not needed * If service is not required start timer now * Remove console.log * Fix lint * Merge * Refactor * Fix tests --- .../case/interceptors/case.transformer.ts | 7 +++--- .../app/modules/case/filters/case.filter.ts | 23 ++++++++----------- .../app/modules/case/filters/cases.filter.ts | 3 +-- .../case/filters/test/cases.filter.spec.ts | 3 +-- .../limitedAccessSubpoena.controller.ts | 1 - .../DefendantInfo/DefendantInfo.css.ts | 2 +- .../SectionHeading/SectionHeading.tsx | 6 ++++- 7 files changed, 22 insertions(+), 23 deletions(-) diff --git a/apps/judicial-system/api/src/app/modules/case/interceptors/case.transformer.ts b/apps/judicial-system/api/src/app/modules/case/interceptors/case.transformer.ts index e4e3ef41187b..4accbfef14f9 100644 --- a/apps/judicial-system/api/src/app/modules/case/interceptors/case.transformer.ts +++ b/apps/judicial-system/api/src/app/modules/case/interceptors/case.transformer.ts @@ -147,9 +147,10 @@ export const getIndictmentInfo = ( const verdictInfo = defendants?.map<[boolean, Date | undefined]>( (defendant) => [ - rulingDecision === CaseIndictmentRulingDecision.RULING && - defendant.serviceRequirement !== ServiceRequirement.NOT_REQUIRED, - defendant.verdictViewDate + rulingDecision === CaseIndictmentRulingDecision.RULING, + defendant.serviceRequirement === ServiceRequirement.NOT_REQUIRED + ? new Date() + : defendant.verdictViewDate ? new Date(defendant.verdictViewDate) : undefined, ], diff --git a/apps/judicial-system/backend/src/app/modules/case/filters/case.filter.ts b/apps/judicial-system/backend/src/app/modules/case/filters/case.filter.ts index 59506016d28b..18a20f17b447 100644 --- a/apps/judicial-system/backend/src/app/modules/case/filters/case.filter.ts +++ b/apps/judicial-system/backend/src/app/modules/case/filters/case.filter.ts @@ -20,7 +20,6 @@ import { isRequestCase, isRestrictionCase, RequestSharedWithDefender, - ServiceRequirement, UserRole, } from '@island.is/judicial-system/types' @@ -289,21 +288,19 @@ const canPrisonAdminUserAccessCase = ( ) { return false } + } - // Check defendant verdict appeal deadline access - const verdictInfo = theCase.defendants?.map<[boolean, Date | undefined]>( - (defendant) => [ - defendant.serviceRequirement !== ServiceRequirement.NOT_REQUIRED, - defendant.verdictViewDate, - ], - ) + // Check defendant verdict appeal deadline access + const canAppealVerdict = true + const verdictInfo = (theCase.defendants || []).map< + [boolean, Date | undefined] + >((defendant) => [canAppealVerdict, defendant.verdictViewDate]) - const [_, indictmentVerdictAppealDeadlineExpired] = - getIndictmentVerdictAppealDeadlineStatus(verdictInfo) + const [_, indictmentVerdictAppealDeadlineExpired] = + getIndictmentVerdictAppealDeadlineStatus(verdictInfo) - if (!indictmentVerdictAppealDeadlineExpired) { - return false - } + if (!indictmentVerdictAppealDeadlineExpired) { + return false } return true diff --git a/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts b/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts index c0df2270d241..c07fe09ae772 100644 --- a/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts +++ b/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts @@ -214,8 +214,7 @@ const getPrisonAdminUserCasesQueryFilter = (): WhereOptions => { [Op.notIn]: Sequelize.literal(` (SELECT case_id FROM defendant - WHERE service_requirement <> 'NOT_REQUIRED' - AND (verdict_view_date IS NULL OR verdict_view_date > NOW() - INTERVAL '28 days')) + WHERE (verdict_view_date IS NULL OR verdict_view_date > NOW() - INTERVAL '28 days')) `), }, }, diff --git a/apps/judicial-system/backend/src/app/modules/case/filters/test/cases.filter.spec.ts b/apps/judicial-system/backend/src/app/modules/case/filters/test/cases.filter.spec.ts index 65c759346088..176476b1ab4f 100644 --- a/apps/judicial-system/backend/src/app/modules/case/filters/test/cases.filter.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/filters/test/cases.filter.spec.ts @@ -389,8 +389,7 @@ describe('getCasesQueryFilter', () => { [Op.notIn]: Sequelize.literal(` (SELECT case_id FROM defendant - WHERE service_requirement <> 'NOT_REQUIRED' - AND (verdict_view_date IS NULL OR verdict_view_date > NOW() - INTERVAL '28 days')) + WHERE (verdict_view_date IS NULL OR verdict_view_date > NOW() - INTERVAL '28 days')) `), }, }, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts index 3db547d3c02c..00ecac0eedcf 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts @@ -6,7 +6,6 @@ import { Header, Inject, Param, - Query, Res, UseGuards, } from '@nestjs/common' diff --git a/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.css.ts b/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.css.ts index 936d96a77249..4393911e52b2 100644 --- a/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.css.ts +++ b/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.css.ts @@ -8,7 +8,7 @@ const baseGridRow = style({ }) export const gridRow = styleVariants({ - withButton: [baseGridRow, { gridTemplateColumns: '5fr 1fr' }], + withButton: [baseGridRow, { gridTemplateColumns: '5fr auto' }], withoutButton: [baseGridRow, { gridTemplateColumns: '1fr' }], }) diff --git a/apps/judicial-system/web/src/components/SectionHeading/SectionHeading.tsx b/apps/judicial-system/web/src/components/SectionHeading/SectionHeading.tsx index cde8643dd52b..a223be1ba987 100644 --- a/apps/judicial-system/web/src/components/SectionHeading/SectionHeading.tsx +++ b/apps/judicial-system/web/src/components/SectionHeading/SectionHeading.tsx @@ -29,7 +29,11 @@ const SectionHeading: FC = ({ {tooltip && ' '} {tooltip && {tooltip}} - {description && {description}} + {description && ( + + {description} + + )} ) From 26fb2c5acff6a96691488ed780d1eca70c145adc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Wed, 16 Oct 2024 09:30:44 +0000 Subject: [PATCH 07/11] feat(contentful-apps): Allow two project subpages to have same slug (#16412) * Allow two subpages to have same slug * Change component name * Remove console.log --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../fields/project-subpage-slug-field.tsx | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 apps/contentful-apps/pages/fields/project-subpage-slug-field.tsx diff --git a/apps/contentful-apps/pages/fields/project-subpage-slug-field.tsx b/apps/contentful-apps/pages/fields/project-subpage-slug-field.tsx new file mode 100644 index 000000000000..fc914059d0cd --- /dev/null +++ b/apps/contentful-apps/pages/fields/project-subpage-slug-field.tsx @@ -0,0 +1,149 @@ +import { useEffect, useRef, useState } from 'react' +import { useDebounce } from 'react-use' +import type { EntryProps } from 'contentful-management' +import { FieldExtensionSDK } from '@contentful/app-sdk' +import { Stack, Text, TextInput } from '@contentful/f36-components' +import { useCMA, useSDK } from '@contentful/react-apps-toolkit' +import slugify from '@sindresorhus/slugify' + +const DEBOUNCE_TIME = 100 + +const ProjectSubpageSlugField = () => { + const sdk = useSDK() + const cma = useCMA() + const [value, setValue] = useState(sdk.field?.getValue() ?? '') + const [isValid, setIsValid] = useState(true) + const [projectPage, setProjectPage] = useState(null) + const initialTitleChange = useRef(true) + const [hasEntryBeenPublished, setHasEntryBeenPublished] = useState( + Boolean(sdk.entry.getSys()?.firstPublishedAt), + ) + + const defaultLocale = sdk.locales.default + + useEffect(() => { + const fetchProjectPage = async () => { + const response = await cma.entry.getMany({ + query: { + links_to_entry: sdk.entry.getSys().id, + content_type: 'projectPage', + 'sys.archivedVersion[exists]': false, + limit: 1, + }, + }) + if (response.items.length > 0) { + setProjectPage(response.items[0]) + } + } + fetchProjectPage() + }, [cma.entry, sdk.entry]) + + useEffect(() => { + sdk.entry.onSysChanged((newSys) => { + setHasEntryBeenPublished(Boolean(newSys?.firstPublishedAt)) + }) + }, [sdk.entry]) + + // Update slug field if the title field changes + useEffect(() => { + return sdk.entry.fields.title + .getForLocale(sdk.field.locale) + .onValueChanged((newTitle) => { + if (hasEntryBeenPublished) { + return + } + + // Callback gets called on initial render, so we want to ignore that + if (initialTitleChange.current) { + initialTitleChange.current = false + return + } + + if (newTitle) { + setValue(slugify(String(newTitle))) + } + }) + }, [hasEntryBeenPublished, sdk.entry.fields.title, sdk.field.locale]) + + useEffect(() => { + sdk.window.startAutoResizer() + }, [sdk.window]) + + // Validate the user input + useDebounce( + async () => { + if (!projectPage) { + setIsValid(true) + return + } + + const subpageIds: string[] = + projectPage?.fields?.projectSubpages?.[defaultLocale]?.map( + (subpage) => subpage.sys.id, + ) ?? [] + + const subpagesWithSameSlug = ( + await cma.entry.getMany({ + query: { + locale: sdk.field.locale, + content_type: 'projectSubpage', + 'fields.slug': value, + 'sys.id[ne]': sdk.entry.getSys().id, + 'sys.archivedVersion[exists]': false, + limit: 1000, + select: 'sys', + }, + }) + ).items + + const subpageExistsWithSameSlug = subpagesWithSameSlug.some((subpage) => + subpageIds.includes(subpage.sys.id), + ) + + if (subpageExistsWithSameSlug) { + setIsValid(false) + return + } + setIsValid(true) + }, + DEBOUNCE_TIME, + [value, projectPage], + ) + + useDebounce( + () => { + if (isValid) { + sdk.field.setValue(value) + } else { + sdk.field.setValue(null) // Set to null to prevent entry publish + } + sdk.field.setInvalid(!isValid) + }, + DEBOUNCE_TIME, + [isValid, value], + ) + + const isInvalid = value.length === 0 || !isValid + + return ( + + { + setValue(ev.target.value) + }} + isInvalid={isInvalid} + /> + {value.length === 0 && sdk.field.locale === defaultLocale && ( + Invalid slug + )} + {value.length > 0 && isInvalid && ( + + Project subpage already exists with this slug + + )} + + ) +} + +export default ProjectSubpageSlugField From 0ae3dac954aaf560b4adeea4aa0e2dafd506df37 Mon Sep 17 00:00:00 2001 From: Kristofer Date: Wed, 16 Oct 2024 09:39:31 +0000 Subject: [PATCH 08/11] chore(personal-representative): Remove `demo-alb` ingress (#16404) * Remove demo-alb from personal-representative * Charts * chore: nx format:write update dirty files --------- Co-authored-by: andes-it Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../infra/personal-representative-public.ts | 9 --------- .../infra/personal-representative.ts | 10 ---------- charts/identity-server/values.dev.yaml | 16 ---------------- charts/identity-server/values.prod.yaml | 8 -------- charts/identity-server/values.staging.yaml | 8 -------- 5 files changed, 51 deletions(-) diff --git a/apps/services/auth/personal-representative-public/infra/personal-representative-public.ts b/apps/services/auth/personal-representative-public/infra/personal-representative-public.ts index e3c409a231df..07d55b20b6bd 100644 --- a/apps/services/auth/personal-representative-public/infra/personal-representative-public.ts +++ b/apps/services/auth/personal-representative-public/infra/personal-representative-public.ts @@ -26,15 +26,6 @@ export const serviceSetup = paths: ['/'], public: false, }, - demo: { - host: { - dev: 'personal-representative-public-xrd.dev01.devland.is', - staging: '', - prod: '', - }, - paths: ['/'], - public: true, - }, }) .readiness('/health/check') .liveness('/liveness') diff --git a/apps/services/auth/personal-representative/infra/personal-representative.ts b/apps/services/auth/personal-representative/infra/personal-representative.ts index 6f10744c5a99..1be694fd7aa3 100644 --- a/apps/services/auth/personal-representative/infra/personal-representative.ts +++ b/apps/services/auth/personal-representative/infra/personal-representative.ts @@ -1,5 +1,4 @@ import { json, service, ServiceBuilder } from '../../../../../infra/src/dsl/dsl' -import { MissingSetting } from '../../../../../infra/src/dsl/types/input-types' import { Base, Client, RskProcuring } from '../../../../../infra/src/dsl/xroad' const REDIS_NODE_CONFIG = { @@ -67,15 +66,6 @@ export const serviceSetup = paths: ['/'], public: false, }, - demo: { - host: { - dev: 'personal-representative-xrd.dev01.devland.is', - staging: MissingSetting, - prod: MissingSetting, - }, - paths: ['/'], - public: true, - }, }) .readiness('/health/check') .liveness('/liveness') diff --git a/charts/identity-server/values.dev.yaml b/charts/identity-server/values.dev.yaml index d50deba82d2a..c9f3dca4c65a 100644 --- a/charts/identity-server/values.dev.yaml +++ b/charts/identity-server/values.dev.yaml @@ -625,14 +625,6 @@ services-auth-personal-representative: image: repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/services-auth-personal-representative' ingress: - demo-alb: - annotations: - kubernetes.io/ingress.class: 'nginx-external-alb' - nginx.ingress.kubernetes.io/service-upstream: 'true' - hosts: - - host: 'personal-representative-xrd.dev01.devland.is' - paths: - - '/' primary-alb: annotations: kubernetes.io/ingress.class: 'nginx-internal-alb' @@ -698,14 +690,6 @@ services-auth-personal-representative-public: image: repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/services-auth-personal-representative-public' ingress: - demo-alb: - annotations: - kubernetes.io/ingress.class: 'nginx-external-alb' - nginx.ingress.kubernetes.io/service-upstream: 'true' - hosts: - - host: 'personal-representative-public-xrd.dev01.devland.is' - paths: - - '/' primary-alb: annotations: kubernetes.io/ingress.class: 'nginx-internal-alb' diff --git a/charts/identity-server/values.prod.yaml b/charts/identity-server/values.prod.yaml index f29985ef17e7..58e9d76a535b 100644 --- a/charts/identity-server/values.prod.yaml +++ b/charts/identity-server/values.prod.yaml @@ -687,14 +687,6 @@ services-auth-personal-representative-public: image: repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/services-auth-personal-representative-public' ingress: - demo-alb: - annotations: - kubernetes.io/ingress.class: 'nginx-external-alb' - nginx.ingress.kubernetes.io/service-upstream: 'true' - hosts: - - host: 'island.is' - paths: - - '/' primary-alb: annotations: kubernetes.io/ingress.class: 'nginx-internal-alb' diff --git a/charts/identity-server/values.staging.yaml b/charts/identity-server/values.staging.yaml index a52d3a746a75..07afb0776b4c 100644 --- a/charts/identity-server/values.staging.yaml +++ b/charts/identity-server/values.staging.yaml @@ -690,14 +690,6 @@ services-auth-personal-representative-public: image: repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/services-auth-personal-representative-public' ingress: - demo-alb: - annotations: - kubernetes.io/ingress.class: 'nginx-external-alb' - nginx.ingress.kubernetes.io/service-upstream: 'true' - hosts: - - host: 'staging01.devland.is' - paths: - - '/' primary-alb: annotations: kubernetes.io/ingress.class: 'nginx-internal-alb' From 330e4c1c39e09a3b2c0747aed21a32bf8a5be0a4 Mon Sep 17 00:00:00 2001 From: albinagu <47886428+albinagu@users.noreply.github.com> Date: Wed, 16 Oct 2024 09:54:35 +0000 Subject: [PATCH 09/11] fix(signature-collections): ongoing updates (#16409) * tweaks * tweak - create list * copylink * chore: nx format:write update dirty files --------- Co-authored-by: andes-it Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../src/fields/CopyLink/index.tsx | 7 +- .../src/forms/Draft.ts | 152 +----------------- .../signature-collection/src/lib/messages.ts | 5 + .../screens/Parliamentary/OwnerView/index.tsx | 16 +- .../src/screens/Parliamentary/index.tsx | 13 +- 5 files changed, 39 insertions(+), 154 deletions(-) diff --git a/libs/application/templates/signature-collection/parliamentary-list-creation/src/fields/CopyLink/index.tsx b/libs/application/templates/signature-collection/parliamentary-list-creation/src/fields/CopyLink/index.tsx index b551805ad303..c47a6bc1ad73 100644 --- a/libs/application/templates/signature-collection/parliamentary-list-creation/src/fields/CopyLink/index.tsx +++ b/libs/application/templates/signature-collection/parliamentary-list-creation/src/fields/CopyLink/index.tsx @@ -7,12 +7,17 @@ import { FC } from 'react' export const CopyLink: FC = ({ application }) => { const { formatMessage } = useLocale() + const { slug } = application.externalData.createLists.data as { slug: string } + return ( {formatMessage(m.shareList)} - + ) } diff --git a/libs/application/templates/signature-collection/parliamentary-list-creation/src/forms/Draft.ts b/libs/application/templates/signature-collection/parliamentary-list-creation/src/forms/Draft.ts index 1cf3776bd6d1..aba2b273173c 100644 --- a/libs/application/templates/signature-collection/parliamentary-list-creation/src/forms/Draft.ts +++ b/libs/application/templates/signature-collection/parliamentary-list-creation/src/forms/Draft.ts @@ -9,7 +9,6 @@ import { buildPhoneField, buildSection, buildSubmitField, - buildTableRepeaterField, buildTextField, getValueViaPath, } from '@island.is/application/core' @@ -20,7 +19,6 @@ import Logo from '../../assets/Logo' import { m } from '../lib/messages' import { formatPhone } from '../lib/utils' -import { Manager, Supervisor } from '../lib/constants' import { Collection } from '@island.is/clients/signature-collection' import { Signee } from '@island.is/clients/signature-collection' @@ -175,89 +173,6 @@ export const Draft: Form = buildForm({ }), ], }), - /* Hiding this screen as for now - buildSection({ - id: 'managers', - title: m.managersAndSupervisors, - children: [ - buildMultiField({ - id: 'managers', - title: m.managersAndSupervisorsTitle, - description: '', - children: [ - buildTableRepeaterField({ - id: 'managers', - title: m.managers, - description: m.managersDescription, - addItemButtonText: m.addManager, - marginTop: 0, - fields: { - manager: { - component: 'nationalIdWithName', - }, - constituency: { - component: 'select', - label: m.constituency, - width: 'full', - options: [ - { - value: m.allConstituencies.defaultMessage, - label: m.allConstituencies, - }, - ], - }, - }, - table: { - header: [m.nationalId, m.name, m.constituency], - rows: ['nationalId', 'name', 'constituency'], - format: { - nationalId: (v) => formatNationalId(v), - }, - }, - }), - buildTableRepeaterField({ - id: 'supervisors', - title: m.supervisors, - description: m.supervisorsDescription, - addItemButtonText: m.addSupervisor, - marginTop: 5, - fields: { - supervisor: { - component: 'nationalIdWithName', - }, - constituency: { - component: 'select', - label: m.constituency, - width: 'full', - isMulti: true, - options: (application) => { - return ( - application.externalData.parliamentaryCollection - .data as Collection - )?.areas.map((area) => ({ - value: `${area.id}|${area.name}`, - label: area.name, - })) - }, - }, - }, - table: { - header: [m.nationalId, m.name, m.constituency], - rows: ['nationalId', 'name', 'constituency'], - format: { - nationalId: (v) => formatNationalId(v), - constituency: (v) => { - return (v as unknown as string[]) - .map((e) => e.split('|')[1]) - .join(', ') - }, - }, - }, - }), - ], - }), - ], - }),*/ buildSection({ id: 'overview', title: m.overview, @@ -363,7 +278,10 @@ export const Draft: Form = buildForm({ title: '', items: ({ answers }) => { return (answers.constituency as string[]).map((c: string) => ({ - heading: 'Flokkur 1 - ' + c.split('|')[1], + heading: + getValueViaPath(answers, 'list.name') + + ' - ' + + c.split('|')[1], progressMeter: { currentProgress: 0, maxProgress: 350, @@ -372,68 +290,6 @@ export const Draft: Form = buildForm({ })) }, }), - /*buildDescriptionField({ - id: 'space4', - title: '', - space: 'gutter', - }), - buildDescriptionField({ - id: 'managersHeader', - title: m.managers, - titleVariant: 'h3', - space: 'gutter', - marginBottom: 3, - condition: (answers) => - !!(answers.managers as Array)?.length, - }), - buildKeyValueField({ - label: '', - width: 'full', - value: ({ answers }) => { - return (answers.managers as Array) - .map( - (m: Manager) => - m.manager.name + - ' - ' + - formatNationalId(m.manager.nationalId) + - ' - ' + - m.constituency, - ) - .join('\n') - }, - }), - buildDescriptionField({ - id: 'space5', - title: '', - space: 'gutter', - }), - buildDescriptionField({ - id: 'supervisorsHeader', - title: m.supervisors, - titleVariant: 'h3', - space: 'gutter', - marginBottom: 3, - condition: (answers) => - !!(answers.supervisors as Array)?.length, - }), - buildKeyValueField({ - label: '', - width: 'full', - value: ({ answers }) => { - return (answers.supervisors as Array) - .map( - (s: Supervisor) => - s.supervisor.name + - ' - ' + - formatNationalId(s.supervisor.nationalId) + - ' - ' + - (s.constituency as unknown as string[]) - .map((c) => c.split('|')[1]) - .join(', '), - ) - .join('\n') - }, - }),*/ buildSubmitField({ id: 'submit', placement: 'footer', diff --git a/libs/service-portal/signature-collection/src/lib/messages.ts b/libs/service-portal/signature-collection/src/lib/messages.ts index 51989608e161..9863b8441aad 100644 --- a/libs/service-portal/signature-collection/src/lib/messages.ts +++ b/libs/service-portal/signature-collection/src/lib/messages.ts @@ -361,6 +361,11 @@ export const m = defineMessages({ defaultMessage: 'Umsjónaraðilar', description: '', }, + noSupervisors: { + id: 'sp.signatureCollection:noSupervisors', + defaultMessage: 'Enginn umsjónaraðili skráður', + description: '', + }, supervisorsTooltip: { id: 'sp.signatureCollection:supervisorsTooltip', defaultMessage: diff --git a/libs/service-portal/signature-collection/src/screens/Parliamentary/OwnerView/index.tsx b/libs/service-portal/signature-collection/src/screens/Parliamentary/OwnerView/index.tsx index 9eba49e00f7f..5a3b627f072c 100644 --- a/libs/service-portal/signature-collection/src/screens/Parliamentary/OwnerView/index.tsx +++ b/libs/service-portal/signature-collection/src/screens/Parliamentary/OwnerView/index.tsx @@ -33,8 +33,11 @@ import { formatNationalId } from '@island.is/portals/core' const OwnerView = ({ currentCollection, + // list holder is an individual who owns a list or has a delegation of type Procuration Holder + isListHolder, }: { currentCollection: SignatureCollection + isListHolder: boolean }) => { const navigate = useNavigate() const location = useLocation() @@ -80,7 +83,8 @@ const OwnerView = ({ color="blue400" /> - {!loadingOwnerLists && + {isListHolder && + !loadingOwnerLists && listsForOwner?.length < currentCollection?.areas.length && ( ( @@ -168,10 +172,12 @@ const OwnerView = ({ /> ), } - : { + : !list.active + ? { label: formatMessage(m.listSubmitted), variant: 'blueberry', } + : undefined } /> @@ -211,13 +217,15 @@ const OwnerView = ({ - ) : ( + ) : collectors.length ? ( collectors.map((collector) => ( {collector.name} {formatNationalId(collector.nationalId)} )) + ) : ( + {formatMessage(m.noSupervisors)} )} diff --git a/libs/service-portal/signature-collection/src/screens/Parliamentary/index.tsx b/libs/service-portal/signature-collection/src/screens/Parliamentary/index.tsx index af20c64d5aad..869a33619124 100644 --- a/libs/service-portal/signature-collection/src/screens/Parliamentary/index.tsx +++ b/libs/service-portal/signature-collection/src/screens/Parliamentary/index.tsx @@ -9,12 +9,15 @@ import { m } from '../../lib/messages' import OwnerView from './OwnerView' import SigneeView from '../shared/SigneeView' import { useGetCurrentCollection, useIsOwner } from '../../hooks' +import { useUserInfo } from '@island.is/auth/react' +import { AuthDelegationType } from '../../types/schema' const SignatureListsParliamentary = () => { useNamespaces('sp.signatureCollection') const { formatMessage } = useLocale() const { isOwner, loadingIsOwner } = useIsOwner() + const userInfo = useUserInfo() const { currentCollection, loadingCurrentCollection } = useGetCurrentCollection() @@ -30,7 +33,15 @@ const SignatureListsParliamentary = () => { {!currentCollection?.isPresidential ? ( isOwner.success ? ( - + ) : ( ) From 0b7c2c6b883bf0b02d65a9b40e95c2d4a8bb0bf1 Mon Sep 17 00:00:00 2001 From: unakb Date: Wed, 16 Oct 2024 10:37:08 +0000 Subject: [PATCH 10/11] feat(j-s): Separate requested defender info from actual assigned defender (#16393) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ívar Oddsson Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../defendant/models/defendant.model.ts | 9 +++ .../20241011090000-update-defendant.js | 73 +++++++++++++++++++ .../defendant/dto/updateDefendant.dto.ts | 15 ++++ .../defendant/models/defendant.model.ts | 16 ++++ .../subpoena/dto/updateSubpoena.dto.ts | 15 ++++ .../app/modules/subpoena/subpoena.service.ts | 14 +++- .../src/app/modules/cases/case.service.ts | 31 ++++---- .../models/internal/internalCase.response.ts | 3 + .../internal/internalDefendant.response.ts | 3 + .../modules/cases/models/subpoena.response.ts | 15 ++-- .../src/components/FormProvider/case.graphql | 3 + .../Advocates/Advocates.strings.ts | 13 ++++ .../Court/Indictments/Advocates/Advocates.tsx | 18 +++++ .../xrd-api/src/app/app.service.ts | 29 ++------ 14 files changed, 211 insertions(+), 46 deletions(-) create mode 100644 apps/judicial-system/backend/migrations/20241011090000-update-defendant.js diff --git a/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts b/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts index 595b696e2c28..f1e16bf6a8aa 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts @@ -75,6 +75,15 @@ export class Defendant { @Field(() => DefenderChoice, { nullable: true }) readonly defenderChoice?: DefenderChoice + @Field(() => DefenderChoice, { nullable: true }) + readonly requestedDefenderChoice?: DefenderChoice + + @Field(() => String, { nullable: true }) + readonly requestedDefenderNationalId?: string + + @Field(() => String, { nullable: true }) + readonly requestedDefenderName?: string + @Field(() => SubpoenaType, { nullable: true }) readonly subpoenaType?: SubpoenaType diff --git a/apps/judicial-system/backend/migrations/20241011090000-update-defendant.js b/apps/judicial-system/backend/migrations/20241011090000-update-defendant.js new file mode 100644 index 000000000000..be8bda849016 --- /dev/null +++ b/apps/judicial-system/backend/migrations/20241011090000-update-defendant.js @@ -0,0 +1,73 @@ +module.exports = { + up: (queryInterface, Sequelize) => { + return queryInterface.sequelize.transaction(async (t) => { + await queryInterface.addColumn( + 'defendant', + 'requested_defender_choice', + { + type: Sequelize.STRING, + allowNull: true, + }, + { transaction: t }, + ) + await queryInterface.addColumn( + 'defendant', + 'requested_defender_national_id', + { + type: Sequelize.STRING, + allowNull: true, + }, + { transaction: t }, + ) + await queryInterface.addColumn( + 'defendant', + 'requested_defender_name', + { + type: Sequelize.STRING, + allowNull: true, + }, + { transaction: t }, + ) + + await queryInterface.sequelize.query( + `UPDATE "defendant" SET requested_defender_choice = defender_choice`, + { transaction: t }, + ) + + await queryInterface.sequelize.query( + `UPDATE "defendant" SET requested_defender_national_id = defender_national_id`, + { transaction: t }, + ) + + await queryInterface.sequelize.query( + `UPDATE "defendant" SET requested_defender_name = defender_name`, + { transaction: t }, + ) + }) + }, + down: (queryInterface) => { + return queryInterface.sequelize.transaction(async (t) => { + await queryInterface.removeColumn( + 'defendant', + 'requested_defender_choice', + { + transaction: t, + }, + ) + await queryInterface.removeColumn( + 'defendant', + 'requested_defender_national_id', + { + transaction: t, + }, + ) + await queryInterface.removeColumn( + 'defendant', + 'requested_defender_name', + { + transaction: t, + }, + ) + }) + }, +} diff --git a/apps/judicial-system/backend/src/app/modules/defendant/dto/updateDefendant.dto.ts b/apps/judicial-system/backend/src/app/modules/defendant/dto/updateDefendant.dto.ts index a2e68e97a596..5c51ae0d2813 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/dto/updateDefendant.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/dto/updateDefendant.dto.ts @@ -93,4 +93,19 @@ export class UpdateDefendantDto { @IsEnum(SubpoenaType) @ApiPropertyOptional({ enum: SubpoenaType }) readonly subpoenaType?: SubpoenaType + + @IsOptional() + @IsEnum(DefenderChoice) + @ApiPropertyOptional({ enum: DefenderChoice }) + readonly requestedDefenderChoice?: DefenderChoice + + @IsOptional() + @IsString() + @ApiPropertyOptional({ type: String }) + readonly requestedDefenderNationalId?: string + + @IsOptional() + @IsString() + @ApiPropertyOptional({ type: String }) + readonly requestedDefenderName?: string } diff --git a/apps/judicial-system/backend/src/app/modules/defendant/models/defendant.model.ts b/apps/judicial-system/backend/src/app/modules/defendant/models/defendant.model.ts index a72b69edeed0..b8c66a3bc14b 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/models/defendant.model.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/models/defendant.model.ts @@ -137,4 +137,20 @@ export class Defendant extends Model { @HasMany(() => Subpoena, { foreignKey: 'defendantId' }) @ApiPropertyOptional({ type: () => Subpoena, isArray: true }) subpoenas?: Subpoena[] + + @Column({ + type: DataType.ENUM, + allowNull: true, + values: Object.values(DefenderChoice), + }) + @ApiPropertyOptional({ enum: DefenderChoice }) + requestedDefenderChoice?: DefenderChoice + + @Column({ type: DataType.STRING, allowNull: true }) + @ApiPropertyOptional({ type: String }) + requestedDefenderNationalId?: string + + @Column({ type: DataType.STRING, allowNull: true }) + @ApiPropertyOptional({ type: String }) + requestedDefenderName?: string } diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts b/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts index 1154f6fb0f81..5132ae3fb5d2 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts @@ -49,4 +49,19 @@ export class UpdateSubpoenaDto { @IsString() @ApiPropertyOptional({ type: String }) readonly defenderPhoneNumber?: string + + @IsOptional() + @IsEnum(DefenderChoice) + @ApiPropertyOptional({ enum: DefenderChoice }) + readonly requestedDefenderChoice?: DefenderChoice + + @IsOptional() + @IsString() + @ApiPropertyOptional({ type: String }) + readonly requestedDefenderNationalId?: string + + @IsOptional() + @IsString() + @ApiPropertyOptional({ type: String }) + readonly requestedDefenderName?: string } diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts index 937d1015920c..c476d085e835 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts @@ -20,7 +20,6 @@ import { type User, } from '@island.is/judicial-system/types' -import { indictment } from '../../messages' import { Case } from '../case/models/case.model' import { PdfService } from '../case/pdf.service' import { Defendant } from '../defendant/models/defendant.model' @@ -93,6 +92,9 @@ export class SubpoenaService { defenderEmail, defenderPhoneNumber, defenderName, + requestedDefenderChoice, + requestedDefenderNationalId, + requestedDefenderName, } = update const [numberOfAffectedRows] = await this.subpoenaModel.update(update, { @@ -102,13 +104,21 @@ export class SubpoenaService { }) let defenderAffectedRows = 0 - if (defenderChoice || defenderNationalId) { + if ( + defenderChoice || + defenderNationalId || + requestedDefenderChoice || + requestedDefenderNationalId + ) { const defendantUpdate: Partial = { defenderChoice, defenderNationalId, defenderName, defenderEmail, defenderPhoneNumber, + requestedDefenderChoice, + requestedDefenderNationalId, + requestedDefenderName, } const [defenderUpdateAffectedRows] = await this.defendantModel.update( diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts index 919a6f7e4fed..03ea716e60d8 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts @@ -117,8 +117,7 @@ export class CaseService { defenderAssignment: UpdateSubpoenaDto, lang?: string, ): Promise { - let defenderChoice = { ...defenderAssignment } - + let chosenLawyer = null if (defenderAssignment.defenderChoice === DefenderChoice.CHOOSE) { if (!defenderAssignment.defenderNationalId) { throw new NotFoundException( @@ -126,7 +125,7 @@ export class CaseService { ) } - const chosenLawyer = await this.lawyersService.getLawyer( + chosenLawyer = await this.lawyersService.getLawyer( defenderAssignment.defenderNationalId, ) @@ -135,20 +134,16 @@ export class CaseService { 'Selected lawyer was not found in the lawyer registry', ) } - - defenderChoice = { - ...defenderChoice, - ...{ - defenderName: chosenLawyer.Name, - defenderEmail: chosenLawyer.Email, - defenderPhoneNumber: chosenLawyer.Phone, - }, - } } - await this.patchSubpoenaInfo(defendantNationalId, caseId, defenderChoice) - + const defenderChoice = { + requestedDefenderChoice: defenderAssignment.defenderChoice, + requestedDefenderNationalId: defenderAssignment.defenderNationalId, + requestedDefenderName: chosenLawyer?.Name, + } + await this.patchDefenseInfo(defendantNationalId, caseId, defenderChoice) const updatedCase = await this.fetchCase(caseId, defendantNationalId) + return SubpoenaResponse.fromInternalCaseResponse( updatedCase, defendantNationalId, @@ -230,10 +225,14 @@ export class CaseService { } } - private async patchSubpoenaInfo( + private async patchDefenseInfo( defendantNationalId: string, caseId: string, - defenderChoice: UpdateSubpoenaDto, + defenderChoice: { + requestedDefenderChoice: DefenderChoice + requestedDefenderNationalId: string | undefined + requestedDefenderName?: string + }, ): Promise { try { const response = await fetch( diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalCase.response.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalCase.response.ts index efff1d029dd3..bb45692f021d 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalCase.response.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalCase.response.ts @@ -30,6 +30,9 @@ interface Defendant { defenderPhoneNumber?: string defenderChoice?: DefenderChoice subpoenas?: Subpoena[] + requestedDefenderChoice?: DefenderChoice + requestedDefenderNationalId?: string + requestedDefenderName?: string } interface DateLog { diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalDefendant.response.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalDefendant.response.ts index 808519a0d05c..1bd4dea18b69 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalDefendant.response.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalDefendant.response.ts @@ -4,4 +4,7 @@ export class InternalDefendantResponse { id!: string defenderChoice?: DefenderChoice defenderName?: string + requestedDefenderChoice?: DefenderChoice + requestedDefenderNationalId?: string + requestedDefenderName?: string } diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/subpoena.response.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/subpoena.response.ts index 0c432b55b5b5..4a5ec483e416 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/subpoena.response.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/subpoena.response.ts @@ -96,8 +96,9 @@ export class SubpoenaResponse { ), ) - const waivedRight = defendantInfo?.defenderChoice === DefenderChoice.WAIVE - const hasDefender = defendantInfo?.defenderName !== null + const waivedRight = + defendantInfo?.requestedDefenderChoice === DefenderChoice.WAIVE + const hasDefender = defendantInfo?.requestedDefenderNationalId !== null const subpoenas = defendantInfo?.subpoenas ?? [] const hasBeenServed = subpoenas.length > 0 && @@ -121,8 +122,8 @@ export class SubpoenaResponse { subtitle: courtNameAndAddress, hasBeenServed: hasBeenServed, hasChosenDefender: Boolean( - defendantInfo?.defenderChoice && - defendantInfo.defenderChoice !== DefenderChoice.DELAY, + defendantInfo?.requestedDefenderChoice && + defendantInfo.requestedDefenderChoice !== DefenderChoice.DELAY, ), defaultDefenderChoice: DefenderChoice.DELAY, alerts: [ @@ -160,12 +161,12 @@ export class SubpoenaResponse { ], }, - defenderInfo: defendantInfo?.defenderChoice + defenderInfo: defendantInfo?.requestedDefenderChoice ? { - defenderChoice: defendantInfo?.defenderChoice, + defenderChoice: defendantInfo?.requestedDefenderChoice, defenderName: !waivedRight && hasDefender - ? defendantInfo?.defenderName + ? defendantInfo?.requestedDefenderName : undefined, canEdit: canChangeDefenseChoice, courtContactInfo: canChangeDefenseChoice diff --git a/apps/judicial-system/web/src/components/FormProvider/case.graphql b/apps/judicial-system/web/src/components/FormProvider/case.graphql index d45521ec30d9..a8afe466ec62 100644 --- a/apps/judicial-system/web/src/components/FormProvider/case.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/case.graphql @@ -22,6 +22,9 @@ query Case($input: CaseQueryInput!) { defenderPhoneNumber defenderChoice defendantPlea + requestedDefenderChoice + requestedDefenderNationalId + requestedDefenderName serviceRequirement verdictViewDate verdictAppealDeadline diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/Advocates.strings.ts b/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/Advocates.strings.ts index 1250e9fa97b9..ac7bfc63dc14 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/Advocates.strings.ts +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/Advocates.strings.ts @@ -74,4 +74,17 @@ export const strings = defineMessages({ description: 'Notaður sem texti þegar enginn lögmaður er skráður í dómaraflæði í ákærum.', }, + defenderChoiceAlertTitle: { + id: 'judicial.system.core:court_indictments.advocates.defender_choice_alert_title', + defaultMessage: 'Val á verjanda - {defendantName}', + description: + 'Notaður sem texti þegar ákærði hefur valið verjanda í dómaraflæði í ákærum.', + }, + defenderChoiceAlertMessage: { + id: 'judicial.system.core:court_indictments.advocates.defender_choice_alert_message', + defaultMessage: + '{requestedDefenderChoice, select, WAIVE {Ég óska ekki eftir verjanda.} CHOOSE {Ég óska þess að valinn lögmaður verji skipaður verjandi minn: {requestedDefenderName} kt. {requestedDefenderNationalId}.} DELAY {Ég óska eftir fresti fram að þingfestingu til þess að tilnefna verjanda.} DELEGATE {Ég fel dómara málsins að tilnefna og skipa mér verjanda.} other {Ekkert valið.}}', + description: + 'Notaður sem texti þegar ákærði hefur valið verjanda í dómaraflæði í ákærum.', + }, }) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/Advocates.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/Advocates.tsx index 09e131c55de5..ed25cda84ea6 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/Advocates.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/Advocates.tsx @@ -59,6 +59,24 @@ const Advocates = () => { message={formatMessage(strings.alertBannerText)} type="info" /> + {workingCase.defendants + ?.filter((defendant) => defendant.requestedDefenderChoice) + .map((defendant) => ( + + + + ))} { - let defenderInfo: { - defenderName: string | undefined - defenderEmail: string | undefined - defenderPhoneNumber: string | undefined - } = { - defenderName: undefined, - defenderEmail: undefined, - defenderPhoneNumber: undefined, - } + let defenderName = undefined if ( updateSubpoena.defenderChoice === DefenderChoice.CHOOSE && @@ -120,11 +112,8 @@ export class AppService { const chosenLawyer = await this.lawyersService.getLawyer( updateSubpoena.defenderNationalId, ) - defenderInfo = { - defenderName: chosenLawyer.Name, - defenderEmail: chosenLawyer.Email, - defenderPhoneNumber: chosenLawyer.Phone, - } + + defenderName = chosenLawyer.Name } catch (reason) { // TODO: Reconsider throwing - what happens if registry is down? this.logger.error( @@ -152,11 +141,9 @@ export class AppService { comment: updateSubpoena.comment, servedBy: updateSubpoena.servedBy, serviceDate: updateSubpoena.servedAt, - defenderChoice: updateSubpoena.defenderChoice, - defenderNationalId: updateSubpoena.defenderNationalId, - defenderName: defenderInfo.defenderName, - defenderEmail: defenderInfo.defenderEmail, - defenderPhoneNumber: defenderInfo.defenderPhoneNumber, + requestedDefenderChoice: updateSubpoena.defenderChoice, + requestedDefenderNationalId: updateSubpoena.defenderNationalId, + requestedDefenderName: defenderName, } try { @@ -178,8 +165,8 @@ export class AppService { return { subpoenaComment: response.comment, defenderInfo: { - defenderChoice: response.defendant.defenderChoice, - defenderName: response.defendant.defenderName, + defenderChoice: response.defendant.requestedDefenderChoice, + defenderName: response.defendant.requestedDefenderName, }, } as SubpoenaResponse } From a74f1fddf40c58ed1cc5f6b03745b051a34ba069 Mon Sep 17 00:00:00 2001 From: Kristofer Date: Wed, 16 Oct 2024 10:59:52 +0000 Subject: [PATCH 11/11] chore(infra): Types for ingress and more (#16405) * Typing for DSL * Force annotations * Revert logic changes * Don't require all keys in mapping * Types for ingress and more * No typecaste in web infra * No typecast in dsl.ts --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- infra/src/dsl/dsl.ts | 3 ++- infra/src/dsl/ingress.spec.ts | 6 +++--- infra/src/dsl/types/charts.ts | 2 +- infra/src/dsl/types/input-types.ts | 16 ++++++++++------ 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/infra/src/dsl/dsl.ts b/infra/src/dsl/dsl.ts index 4ea9b97b439e..3317e32736e3 100644 --- a/infra/src/dsl/dsl.ts +++ b/infra/src/dsl/dsl.ts @@ -17,6 +17,7 @@ import { ServiceDefinition, XroadConfig, PodDisruptionBudget, + IngressMapping, } from './types/input-types' import { logger } from '../logging' import { COMMON_SECRETS } from './consts' @@ -461,7 +462,7 @@ export class ServiceBuilder { * You can allow ingress traffic (traffic from the internet) to your service by creating an ingress controller. Mapped to an [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/#what-is-ingress). * @param ingress - Ingress parameters */ - ingress(ingress: { [name: string]: Ingress }) { + ingress(ingress: IngressMapping) { this.serviceDef.ingress = ingress return this } diff --git a/infra/src/dsl/ingress.spec.ts b/infra/src/dsl/ingress.spec.ts index dc1eaed43b53..142050bbb82c 100644 --- a/infra/src/dsl/ingress.spec.ts +++ b/infra/src/dsl/ingress.spec.ts @@ -27,7 +27,7 @@ describe('Ingress definitions', () => { host: { dev: 'a', staging: 'a', prod: 'a' }, paths: ['/api'], }, - secondary: { + internal: { host: { dev: 'b', staging: 'b', prod: 'b' }, paths: ['/'], }, @@ -47,7 +47,7 @@ describe('Ingress definitions', () => { }, hosts: [{ host: 'a.staging01.devland.is', paths: ['/api'] }], }, - 'secondary-alb': { + 'internal-alb': { annotations: { 'kubernetes.io/ingress.class': 'nginx-external-alb', 'nginx.ingress.kubernetes.io/service-upstream': 'true', @@ -97,7 +97,7 @@ describe('Ingress definitions', () => { }, paths: ['/api'], }, - secondary: { + internal: { host: { dev: MissingSetting, staging: MissingSetting, diff --git a/infra/src/dsl/types/charts.ts b/infra/src/dsl/types/charts.ts index d59737a1e18b..aeb21551890d 100644 --- a/infra/src/dsl/types/charts.ts +++ b/infra/src/dsl/types/charts.ts @@ -36,6 +36,6 @@ export type OpsEnvName = | 'prod' | 'prod-ids' -export type EnvironmentServices = { [name in OpsEnv]: ServiceBuilder[] } +export type EnvironmentServices = { [env in OpsEnv]: ServiceBuilder[] } export type EnvironmentConfigs = { [name in OpsEnvName]: EnvironmentConfig } diff --git a/infra/src/dsl/types/input-types.ts b/infra/src/dsl/types/input-types.ts index dfc428a9c5bd..9c18118b2e0a 100644 --- a/infra/src/dsl/types/input-types.ts +++ b/infra/src/dsl/types/input-types.ts @@ -18,7 +18,7 @@ export type ValueType = MissingSettingType | ValueSource export type AccessModes = 'ReadWrite' | 'ReadOnly' export type PostgresInfo = { host?: { - [idx in OpsEnv]: string + [env in OpsEnv]: string } name?: string username?: string @@ -35,7 +35,7 @@ export type PostgresInfoForEnv = { } export type RedisInfo = { - host?: { [idx in OpsEnv]: string } + host?: { [env in OpsEnv]: string } } export type RedisInfoForEnv = { @@ -106,13 +106,15 @@ export type ServiceDefinitionCore = { volumes: PersistentVolumeClaim[] podDisruptionBudget?: PodDisruptionBudget } +export type IngressType = 'primary' | 'internal' +export type IngressMapping = Partial<{ [name in IngressType]: Ingress }> /** * This is the definition of a service as generated by ServiceBuilder. */ export type ServiceDefinition = ServiceDefinitionCore & { initContainers?: InitContainers env: EnvironmentVariables - ingress: { [name: string]: Ingress } + ingress: IngressMapping postgres?: PostgresInfo redis?: RedisInfo extraAttributes?: ExtraValues @@ -133,11 +135,13 @@ export type ServiceDefinitionForEnv = ServiceDefinitionCore & { export interface Ingress { host: { - [name in OpsEnv]: string | string[] + [env in OpsEnv]: string | string[] } paths: string[] public?: boolean - extraAnnotations?: { [name in OpsEnv]: { [idx: string]: string | null } } + extraAnnotations?: { + [env in OpsEnv]: { [annotation: string]: string | null } + } } export interface IngressForEnv { @@ -227,4 +231,4 @@ export interface Context { } export type ExtraValuesForEnv = Hash -export type ExtraValues = { [idx in OpsEnv]: Hash | MissingSettingType } +export type ExtraValues = { [env in OpsEnv]: Hash | MissingSettingType }