Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(signature-collection): Sign parliamentary list hooked up #15927

Merged
merged 9 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ export class SignatureCollectionCandidate {

@Field({ nullable: true })
collectionId?: string

@Field({ nullable: true })
partyBallotLetter?: string
juni-haukur marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ import { IdCardModule } from './id-card/id-card.module'
import { IdCardService } from './id-card/id-card.service'
import { ParliamentaryListCreationModule } from './signature-collection/parliamentary-list-creation/parliamentary-list-creation.module'
import { ParliamentaryListCreationService } from './signature-collection/parliamentary-list-creation/parliamentary-list-creation.service'
import { ParliamentaryListSigningModule } from './signature-collection/parliamentary-list-signing/parliamentary-list-signing.module'
import { ParliamentaryListSigningService } from './signature-collection/parliamentary-list-signing/parliamentary-list-signing.service'

export const modules = [
ReferenceTemplateModule,
Expand Down Expand Up @@ -213,6 +215,7 @@ export const modules = [
HealthInsuranceDeclarationModule,
NewPrimarySchoolModule,
ParliamentaryListCreationModule,
ParliamentaryListSigningModule,
]

export const services = [
Expand Down Expand Up @@ -285,4 +288,5 @@ export const services = [
HealthInsuranceDeclarationService,
NewPrimarySchoolService,
ParliamentaryListCreationService,
ParliamentaryListSigningService,
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { DynamicModule } from '@nestjs/common'

// This is a shared module that gives you access to common methods
import { SharedTemplateAPIModule } from '../../../shared'

// The base config that template api modules are registered with by default
// (configurable inside `template-api.module.ts`)
import { BaseTemplateAPIModuleConfig } from '../../../../types'

// Here you import your module service
import { ParliamentaryListSigningService } from './parliamentary-list-signing.service'
import { SignatureCollectionClientModule } from '@island.is/clients/signature-collection'

export class ParliamentaryListSigningModule {
static register(config: BaseTemplateAPIModuleConfig): DynamicModule {
return {
module: ParliamentaryListSigningModule,
imports: [
SharedTemplateAPIModule.register(config),
SignatureCollectionClientModule,
],
providers: [ParliamentaryListSigningService],
exports: [ParliamentaryListSigningService],
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { Injectable } from '@nestjs/common'
import { TemplateApiModuleActionProps } from '../../../../types'
import { BaseTemplateApiService } from '../../../base-template-api.service'
import { ApplicationTypes } from '@island.is/application/types'
import {
List,
ReasonKey,
SignatureCollectionClientService,
} from '@island.is/clients/signature-collection'
import { TemplateApiError } from '@island.is/nest/problem'
import { errorMessages } from '@island.is/application/templates/signature-collection/parliamentary-list-signing'
import { ProviderErrorReason } from '@island.is/shared/problem'

@Injectable()
export class ParliamentaryListSigningService extends BaseTemplateApiService {
constructor(
private signatureCollectionClientService: SignatureCollectionClientService,
) {
super(ApplicationTypes.PARLIAMENTARY_LIST_SIGNING)
}

async signList({ auth, application }: TemplateApiModuleActionProps) {
const listId = application.answers.listId
? (application.answers.listId as string)
: (application.externalData.getList.data as List[])[0].id

const signature = await this.signatureCollectionClientService.signList(
listId,
auth,
)
if (signature) {
return signature
} else {
throw new TemplateApiError(errorMessages.submitFailure, 405)
}
}

async canSign({ auth }: TemplateApiModuleActionProps) {
const signee = await this.signatureCollectionClientService.getSignee(auth)
const { canSign, canSignInfo } = signee
if (canSign) {
return signee
}
if (!canSignInfo) {
// canCreateInfo will always be defined if canCreate is false but we need to check for typescript
throw new TemplateApiError(errorMessages.deniedByService, 400)
}
const errors: ProviderErrorReason[] = canSignInfo?.map((key) => {
switch (key) {
case ReasonKey.UnderAge:
return errorMessages.age
case ReasonKey.NoCitizenship:
return errorMessages.citizenship
case ReasonKey.NotISResidency:
return errorMessages.residency
case ReasonKey.CollectionNotOpen:
return errorMessages.active
case ReasonKey.AlreadySigned:
return errorMessages.signer
case ReasonKey.noInvalidSignature:
return errorMessages.invalidSignature
default:
return errorMessages.deniedByService
}
})
throw new TemplateApiError(errors, 405)
}

async getList({ auth, application }: TemplateApiModuleActionProps) {
// Returns the list user is trying to sign, in the apporiate area
const areaId = (
application.externalData.canSign.data as { area: { id: string } }
).area?.id

if (!areaId) {
// If no area user will be stopped by can sign above
return new TemplateApiError(errorMessages.areaId, 400)
}
const ownerId = application.answers.initialQuery as string
// Check if user got correct ownerId, if not user has to pick list
const isCandidateId =
await this.signatureCollectionClientService.isCandidateId(ownerId, auth)

// If initialQuery is not defined return all list for area
const lists = await this.signatureCollectionClientService.getLists({
nationalId: auth.nationalId,
candidateId: isCandidateId ? ownerId : undefined,
areaId,
onlyActive: true,
})
// If candidateId existed or if there is only one list, check if maxReached
if (lists.length === 1) {
const { maxReached } = lists[0]
if (maxReached) {
throw new TemplateApiError(errorMessages.maxReached, 405)
}
}
if (lists.length === 0) {
throw new TemplateApiError(errorMessages.active, 404)
}

return lists
}
juni-haukur marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,13 @@ export const OwnerRequirementsApi = defineTemplateApi({
export const CurrentCollectionApi = defineTemplateApi({
action: 'currentCollection',
})

export const CanSignApi = defineTemplateApi({
action: 'canSign',
order: 0,
})

export const GetListApi = defineTemplateApi({
action: 'getList',
order: 1,
})
juni-haukur marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import {
buildMessageWithLinkButtonField,
buildDescriptionField,
} from '@island.is/application/core'
import { Form, FormModes } from '@island.is/application/types'
import { Application, Form, FormModes } from '@island.is/application/types'
import { m } from '../lib/messages'
import { infer as zinfer } from 'zod'
import { dataSchema } from '../lib/dataSchema'
type Answers = zinfer<typeof dataSchema>

export const Done: Form = buildForm({
id: 'done',
Expand All @@ -32,7 +35,12 @@ export const Done: Form = buildForm({
buildMultiField({
id: 'doneScreen',
title: m.listSigned,
description: m.listSignedDescription,
description: (application: Application) => ({
...m.listSignedDescription,
values: {
name: (application.answers as Answers).list.name,
},
}),
children: [
buildMessageWithLinkButtonField({
id: 'done.goToServicePortal',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import {
buildDescriptionField,
buildForm,
buildHiddenInput,
buildMultiField,
buildSection,
buildSubmitField,
buildTextField,
getValueViaPath,
} from '@island.is/application/core'
import { DefaultEvents, Form, FormModes } from '@island.is/application/types'
import { Application } from '@island.is/api/schema'
import { Application, SignatureCollectionList } from '@island.is/api/schema'
import { format as formatNationalId } from 'kennitala'
import Logo from '../../assets/Logo'

Expand Down Expand Up @@ -45,19 +47,66 @@ export const Draft: Form = buildForm({
title: m.listHeader,
titleVariant: 'h3',
}),
buildHiddenInput({
id: 'listId',
defaultValue: ({ answers, externalData }: Application) => {
const lists = getValueViaPath(
externalData,
'getList.data',
[],
) as SignatureCollectionList[]

const initialQuery = getValueViaPath(
answers,
'initialQuery',
'',
)

return lists.find((x) => x.candidate.id === initialQuery)?.id
},
}),
buildTextField({
id: 'list.name',
title: m.listName,
width: 'half',
readOnly: true,
defaultValue: 'Flokkur 1',
defaultValue: ({ answers, externalData }: Application) => {
const lists = getValueViaPath(
externalData,
'getList.data',
[],
) as SignatureCollectionList[]

const initialQuery = getValueViaPath(
answers,
'initialQuery',
'',
)

return lists.find((x) => x.candidate.id === initialQuery)?.title
},
}),
buildTextField({
id: 'list.letter',
title: m.listLetter,
width: 'half',
readOnly: true,
defaultValue: 'F',
defaultValue: ({ answers, externalData }: Application) => {
const lists = getValueViaPath(
externalData,
'getList.data',
[],
) as SignatureCollectionList[]

const initialQuery = getValueViaPath(
answers,
'initialQuery',
'',
)

return lists.find((x) => x.candidate.id === initialQuery)
?.candidate?.partyBallotLetter
},
}),
buildDescriptionField({
id: 'signeeHeader',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {

import { m } from '../lib/messages'
import Logo from '../../assets/Logo'
import { CanSignApi, GetListApi } from '../dataProviders'

export const Prerequisites: Form = buildForm({
id: 'SignListPrerequisites',
Expand Down Expand Up @@ -76,6 +77,16 @@ export const Prerequisites: Form = buildForm({
title: m.nationalRegistryProviderTitle,
subTitle: m.nationalRegistryProviderSubtitle,
}),
buildDataProviderItem({
provider: CanSignApi,
title: '',
subTitle: '',
}),
buildDataProviderItem({
provider: GetListApi,
title: '',
subTitle: '',
}),
buildDataProviderItem({
//provider: TODO: Add providers needed for signing collection,
title: '',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import { z } from 'zod'
export const dataSchema = z.object({
/* Gagnaöflun */
approveExternalData: z.boolean().refine((v) => v),
listId: z.string().min(1),
list: z.object({
name: z.string(),
letter: z.string(),
}),
juni-haukur marked this conversation as resolved.
Show resolved Hide resolved

/* Upplýsingar */
signee: z.object({
Expand Down
Loading
Loading