Skip to content

Commit

Permalink
claims can be converted
Browse files Browse the repository at this point in the history
  • Loading branch information
jlarsson committed Sep 21, 2023
1 parent 65d6f8e commit 4e0e863
Show file tree
Hide file tree
Showing 8 changed files with 231 additions and 7 deletions.
10 changes: 7 additions & 3 deletions src/adverts/advert-meta/advert-meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,17 @@ export const getAdvertMeta = (advert: Advert, user: HaffaUser): AdvertMeta => {
isNotArchived &&
(myReservationCount > 0 || quantity > claimCount) &&
canCollectAdverts,
canCancelClaim:
canManageClaims:
canManageOwnAdvertsHistory && (mine || canManageAllAdverts),
reservedyMe: myReservationCount,
collectedByMe: myCollectedCount,
claims:
canManageOwnAdvertsHistory && (mine || canManageAllAdverts)
? advert.claims
? advert.claims.map(c => ({
...c,
canCancel: true,
canConvert: true,
}))
: [],
}
}
Expand All @@ -73,7 +77,7 @@ export const getAdvertMeta = (advert: Advert, user: HaffaUser): AdvertMeta => {
canReserve: false,
canCancelReservation: false,
canCollect: false,
canCancelClaim: false,
canManageClaims: false,
reservedyMe: myReservationCount,
collectedByMe: myCollectedCount,
claims: [],
Expand Down
2 changes: 1 addition & 1 deletion src/adverts/advert-mutations/claims/cancel-advert-claim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const createCancelAdvertClaim =
.load(() => adverts.getAdvert(user, id))
.validate(async (advert, { throwIf }) =>
throwIf(
!getAdvertMeta(advert, user).canCancelClaim,
!getAdvertMeta(advert, user).canManageClaims,
TxErrors.Unauthorized
)
)
Expand Down
114 changes: 114 additions & 0 deletions src/adverts/advert-mutations/claims/convert-advert-claim.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import {
T,
createTestNotificationServices,
end2endTest,
} from '../../../test-utils'
import { createEmptyAdvert } from '../../mappers'
import type { AdvertMutationResult } from '../../types'
import { AdvertClaimType } from '../../types'
import { mutationProps } from '../test-utils/gql-test-definitions'

const convertAdvertClaimMutation = /* GraphQL */ `
mutation Mutation(
$id: ID!,
$by: String!,
$type: AdvertClaimType!
$newType: AdvertClaimType!
) {
convertAdvertClaim(id: $id, by: $by, type: $type, newType: $newType) {
${mutationProps}
}
}
`
describe('convertAdvertClaim', () => {
it('can convert reservation to collect', () => {
const advertWasCollected = jest.fn(async () => void 0)
const notifications = createTestNotificationServices({
advertWasCollected,
})
return end2endTest(
{
services: { notifications },
},
async ({ mappedGqlRequest, adverts, user, loginPolicies }) => {
// give us rights to handle claims
await loginPolicies.updateLoginPolicies([
{
emailPattern: user.id,
roles: ['canManageOwnAdvertsHistory'],
},
])
// eslint-disable-next-line no-param-reassign
adverts['advert-123'] = {
...createEmptyAdvert(),
id: 'advert-123',
createdBy: user.id,
quantity: 50,
claims: [
{
by: 'someone I used to know',
at: '',
quantity: 2,
type: AdvertClaimType.reserved,
},
{
by: user.id,
at: '',
quantity: 1,
type: AdvertClaimType.reserved,
},
{
by: 'someone else',
at: '',
quantity: 1,
type: AdvertClaimType.reserved,
},
],
}

const result = await mappedGqlRequest<AdvertMutationResult>(
'convertAdvertClaim',
convertAdvertClaimMutation,
{
id: 'advert-123',
by: user.id,
type: AdvertClaimType.reserved,
newType: AdvertClaimType.collected,
}
)
expect(result.status).toBeNull()

T('claim should be updated in database', () =>
expect(adverts['advert-123'].claims).toMatchObject([
{
by: 'someone I used to know',
at: '',
quantity: 2,
type: 'reserved',
},
{
by: 'someone else',
at: '',
quantity: 1,
type: 'reserved',
},
{
by: user.id,
// at: '',
quantity: 1,
type: AdvertClaimType.collected,
},
])
)

T('should have notified about the interesting event', () =>
expect(advertWasCollected).toHaveBeenCalledWith(
user,
1,
adverts['advert-123']
)
)
}
)
})
})
76 changes: 76 additions & 0 deletions src/adverts/advert-mutations/claims/convert-advert-claim.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { TxErrors, txBuilder } from '../../../transactions'
import type { Services } from '../../../types'
import { getAdvertMeta } from '../../advert-meta'
import { AdvertClaimType } from '../../types'
import type { AdvertClaim, Advert, AdvertMutations } from '../../types'
import { mapTxResultToAdvertMutationResult } from '../mappers'
import {
verifyAll,
verifyReservationLimits,
verifyReservationsDoesNotExceedQuantity,
verifyTypeIsReservation,
} from '../verifiers'

export const createConvertAdvertClaim =
({
adverts,
notifications,
}: Pick<
Services,
'adverts' | 'notifications'
>): AdvertMutations['convertAdvertClaim'] =>
(user, id, by, type, newType) =>
txBuilder<Advert>()
.load(() => adverts.getAdvert(user, id))
.validate(async (advert, { throwIf }) =>
throwIf(
!getAdvertMeta(advert, user).canManageClaims,
TxErrors.Unauthorized
)
)
.patch((advert, { actions }) => {
const matchClaim = (c: AdvertClaim) =>
c.by === by && c.type === type && c.type !== newType
const claims = advert.claims.filter(matchClaim)
if (claims.length === 0) {
return null
}

claims.forEach(claim =>
actions(patched =>
newType === AdvertClaimType.collected
? notifications.advertWasCollected(
{ id: by },
claim.quantity,
patched
)
: Promise.resolve()
)
)
return {
...advert,
claims: advert.claims
.filter(c => !matchClaim(c))
.concat(
claims.map(c => ({
...c,
at: new Date().toISOString(),
type: newType,
}))
)
.filter(({ quantity }) => quantity > 0),
}
})
.verify((_, ctx) =>
verifyAll(
ctx,
verifyTypeIsReservation,
verifyReservationLimits,
verifyReservationsDoesNotExceedQuantity
)
)
.saveVersion((versionId, advert) =>
adverts.saveAdvertVersion(user, versionId, advert)
)
.run()
.then(mapTxResultToAdvertMutationResult)
2 changes: 2 additions & 0 deletions src/adverts/advert-mutations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
createRemoveAdvert,
createUpdateAdvert,
} from './crud'
import { createConvertAdvertClaim } from './claims/convert-advert-claim'

export const createAdvertMutations = (
services: Pick<Services, 'adverts' | 'files' | 'notifications'>
Expand All @@ -25,4 +26,5 @@ export const createAdvertMutations = (
archiveAdvert: createArchiveAdvert(services),
unarchiveAdvert: createUnarchiveAdvert(services),
cancelAdvertClaim: createCancelAdvertClaim(services),
convertAdvertClaim: createConvertAdvertClaim(services),
})
9 changes: 9 additions & 0 deletions src/adverts/adverts-gql-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,15 @@ export const createAdvertsGqlModule = (
.then(result =>
mapAdvertMutationResultToAdvertWithMetaMutationResult(user, result)
),
convertAdvertClaim: async ({
ctx: { user },
args: { id, by, type, newType },
}) =>
createAdvertMutations(services)
.convertAdvertClaim(user, id, by, type, newType)
.then(result =>
mapAdvertMutationResultToAdvertWithMetaMutationResult(user, result)
),
},
},
})
10 changes: 9 additions & 1 deletion src/adverts/adverts.gql.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ export const advertsGqlSchema = /* GraphQL */ `
by: String!
type: AdvertClaimType!
): AdvertMutationResult
convertAdvertClaim(
id: ID!
by: String!
type: AdvertClaimType!
newType: AdvertClaimType!
): AdvertMutationResult
}
type AdvertMutationStatus {
Expand Down Expand Up @@ -119,6 +125,8 @@ export const advertsGqlSchema = /* GraphQL */ `
by: String
at: String
type: AdvertClaimType
canCancel: Boolean
canConvert: Boolean
}
type AdvertMeta {
Expand All @@ -133,7 +141,7 @@ export const advertsGqlSchema = /* GraphQL */ `
canReserve: Boolean!
canCancelReservation: Boolean!
canCollect: Boolean!
canCancelClaim: Boolean!
canManageClaims: Boolean!
reservedyMe: Int!
collectedByMe: Int!
claims: [AdvertClaim]!
Expand Down
15 changes: 13 additions & 2 deletions src/adverts/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ export interface AdvertUserFields {
contact: AdvertContact
}

export interface AdvertMetaClaim extends AdvertClaim {
canCancel: boolean
canConvert: boolean
}
export interface AdvertMeta {
reservableQuantity: number
collectableQuantity: number
Expand All @@ -45,10 +49,10 @@ export interface AdvertMeta {
canReserve: boolean
canCancelReservation: boolean
canCollect: boolean
canCancelClaim: boolean
canManageClaims: boolean
reservedyMe: number
collectedByMe: number
claims: AdvertClaim[]
claims: AdvertMetaClaim[]
}

export interface AdvertMutationStatus {
Expand Down Expand Up @@ -201,6 +205,13 @@ export interface AdvertMutations {
by: string,
type: AdvertClaimType
) => Promise<AdvertMutationResult>
convertAdvertClaim: (
user: HaffaUser,
id: string,
by: string,
type: AdvertClaimType,
newType: AdvertClaimType
) => Promise<AdvertMutationResult>
}

interface AdvertStats {
Expand Down

0 comments on commit 4e0e863

Please sign in to comment.