Skip to content

Commit

Permalink
Feat/pick on collect (#39)
Browse files Browse the repository at this point in the history
* add pick on collect flag

* add testcases

* change pickoncollect to property

---------

Co-authored-by: Petter Andersson <[email protected]>
  • Loading branch information
petter-a and Petter Andersson authored Jan 30, 2025
1 parent 450aabc commit 2f224a7
Show file tree
Hide file tree
Showing 14 changed files with 185 additions and 10 deletions.
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,7 @@ PASSWORDLESS_FIXED_PINCODE=123456
#------------------------------------------------------------
# Number of days to retent systemlog data
#SYSLOG_RETENTION_DAYS=10

#------------------------------------------------------------
# Automatically set advert to picked when collected
#PICK_ON_COLLECT=0
1 change: 0 additions & 1 deletion src/adverts/advert-mutations/claims/cancel-advert-claim.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { makeUser } from '../../../login'
import { TxErrors, txBuilder } from '../../../transactions'
import type { Services } from '../../../types'
import { normalizeAdvertClaims } from '../../advert-claims'
Expand Down
69 changes: 68 additions & 1 deletion src/adverts/advert-mutations/claims/convert-advert-claim.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { makeUser } from '../../../login'
import {
T,
createTestNotificationServices,
Expand Down Expand Up @@ -137,3 +136,71 @@ describe('convertAdvertClaim', () => {
)
})
})
describe('convertAdvertClaim - picking', () => {
it('should set picked at collect', () => {
const advertWasCollected = jest.fn(async () => undefined)
const advertWasCollectedOwner = jest.fn(async () => undefined)
const notifications = createTestNotificationServices({
advertWasCollected,
advertWasCollectedOwner,
})
const workflow = {
get pickOnCollect() {
return true
},
}
const spy = jest.spyOn(workflow, 'pickOnCollect', 'get')

return end2endTest(
{
services: { notifications, workflow },
},
async ({ mappedGqlRequest, adverts, user, loginPolicies }) => {
// give us rights to handle claims
await loginPolicies.updateLoginPolicies([
{
emailPattern: user.id,
roles: ['canManageOwnAdvertsHistory', 'canManageAllAdverts'],
},
])
// eslint-disable-next-line no-param-reassign
adverts['advert-123'] = {
...createEmptyAdvert(),
id: 'advert-123',
createdBy: 'some@owner',
quantity: 50,
claims: [
{
by: 'claims@user',
at: '',
quantity: 2,
type: AdvertClaimType.reserved,
events: [],
},
],
}

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

T('advert should be updated in database', () =>
expect(adverts['advert-123'].pickedAt).toHaveLength(
'2025-01-17T13:35:25.725Z'.length
)
)
T('should have checked configuration', () =>
expect(spy).toHaveBeenCalled()
)
}
)
})
})
21 changes: 17 additions & 4 deletions src/adverts/advert-mutations/claims/convert-advert-claim.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { makeUser } from '../../../login'
import { TxErrors, txBuilder } from '../../../transactions'
import type { Services } from '../../../types'
import { normalizeAdvertClaims } from '../../advert-claims'
import { getAdvertMeta } from '../../advert-meta'
import type { AdvertClaim, Advert, AdvertMutations } from '../../types'
import {
type AdvertClaim,
type Advert,
type AdvertMutations,
AdvertClaimType,
} from '../../types'
import { mapTxResultToAdvertMutationResult } from '../mappers'
import {
verifyAll,
Expand All @@ -17,9 +21,10 @@ export const createConvertAdvertClaim =
({
adverts,
notifications,
workflow: { pickOnCollect },
}: Pick<
Services,
'adverts' | 'notifications'
'adverts' | 'notifications' | 'workflow'
>): AdvertMutations['convertAdvertClaim'] =>
(user, id, by, type, newType, impersonate) =>
txBuilder<Advert>()
Expand All @@ -31,6 +36,8 @@ export const createConvertAdvertClaim =
)
)
.patch((advert, { actions }) => {
const at = new Date().toISOString()

const matchClaim = (c: AdvertClaim) =>
c.by === by && c.type === type && c.type !== newType

Expand All @@ -41,7 +48,7 @@ export const createConvertAdvertClaim =

const updatedClaims = claims.map(c => ({
...c,
at: new Date().toISOString(),
at,
type: newType,
events: [],
}))
Expand All @@ -55,8 +62,14 @@ export const createConvertAdvertClaim =
impersonate || null
)
)
const pickedAt =
newType === AdvertClaimType.collected && pickOnCollect
? at
: advert.pickedAt

return {
...advert,
pickedAt,
claims: normalizeAdvertClaims(
advert.claims.filter(c => !matchClaim(c)).concat(updatedClaims)
),
Expand Down
55 changes: 55 additions & 0 deletions src/adverts/advert-mutations/collecting/collect-advert.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,59 @@ describe('collectAdvert', () => {
}
)
})

it('should set picked at collect', () => {
const advertWasCollected = jest.fn(async () => undefined)
const advertWasCollectedOwner = jest.fn(async () => undefined)
const notifications = createTestNotificationServices({
advertWasCollected,
advertWasCollectedOwner,
})
const workflow = {
get pickOnCollect() {
return true
},
}
const spy = jest.spyOn(workflow, 'pickOnCollect', 'get')

return end2endTest(
{ services: { notifications, workflow } },
async ({ mappedGqlRequest, adverts, user, loginPolicies }) => {
// give us rights to collect
await loginPolicies.updateLoginPolicies([
{
emailPattern: user.id,
roles: ['canCollectAdverts'],
},
])

// eslint-disable-next-line no-param-reassign
adverts['advert-123'] = {
...createEmptyAdvert(),
id: 'advert-123',
quantity: 1,
}

const result = await mappedGqlRequest<AdvertMutationResult>(
'collectAdvert',
collectAdvertMutation,
{
id: 'advert-123',
quantity: 1,
}
)
expect(result.status).toBeNull()

T('should be updated in database', () =>
expect(adverts['advert-123'].pickedAt).toHaveLength(
'2025-01-17T13:35:25.725Z'.length
)
)

T('should have checked configuration', () =>
expect(spy).toHaveBeenCalled()
)
}
)
})
})
7 changes: 6 additions & 1 deletion src/adverts/advert-mutations/collecting/collect-advert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ export const createCollectAdvert =
({
adverts,
notifications,
workflow: { pickOnCollect },
}: Pick<
Services,
'adverts' | 'notifications'
'adverts' | 'notifications' | 'workflow'
>): AdvertMutations['reserveAdvert'] =>
(user, id, quantity) =>
txBuilder<Advert>()
Expand Down Expand Up @@ -61,8 +62,12 @@ export const createCollectAdvert =
)
.map(({ quantity }) => quantity)
.reduce((s, v) => s + v, 0)

const pickedAt = pickOnCollect ? at : advert.pickedAt

return {
...advert,
pickedAt,
claims: normalizeAdvertClaims([
...advert.claims.filter(({ by }) => by !== user.id), // all except mine
{
Expand Down
5 changes: 4 additions & 1 deletion src/adverts/advert-mutations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ import { createMarkAdvertAsUnpicked } from './picked/mark-advert-as-unpicked'
import { createPatchAdvertTags } from './tags/patch-advert-tags'

export const createAdvertMutations = (
services: Pick<Services, 'adverts' | 'files' | 'notifications' | 'syslog'>
services: Pick<
Services,
'adverts' | 'files' | 'notifications' | 'syslog' | 'workflow'
>
): AdvertMutations => ({
importAdvertSnapshot: createImportAdvertSnapshot(services),
createAdvert: createCreateAdvert(services),
Expand Down
2 changes: 1 addition & 1 deletion src/adverts/adverts-gql-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { GraphQLModule } from '../lib/gdi-api-node'
export const createAdvertsGqlModule = (
services: Pick<
Services,
'adverts' | 'categories' | 'files' | 'notifications' | 'syslog'
'adverts' | 'categories' | 'files' | 'notifications' | 'syslog' | 'workflow'
>
): GraphQLModule => ({
schema: advertsGqlSchema,
Expand Down
7 changes: 6 additions & 1 deletion src/jobs/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import type { Services } from '../types'

export type JobServices = Pick<
Services,
'syslog' | 'notifications' | 'adverts' | 'files' | 'subscriptions'
| 'syslog'
| 'notifications'
| 'adverts'
| 'files'
| 'subscriptions'
| 'workflow'
>
export type TaskRunnerSignature = (
services: JobServices,
Expand Down
4 changes: 4 additions & 0 deletions src/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { createEventLogServiceFromEnv } from './events'
import { createSubscriptionsRepositoryFromEnv } from './subscriptions'
import { createContentRepositoryFromEnv } from './content'
import { createSyslogServiceFromEnv } from './syslog'
import { createWorkflowServiceFromEnv } from './workflow'

const createStartupLog = (): StartupLog => ({
echo: (service, { name, config }) => {
Expand All @@ -28,6 +29,7 @@ const createStartupLog = (): StartupLog => ({
})

const createServicesFromEnv = (): Services => {
const workflow = createWorkflowServiceFromEnv()
const startupLog = createStartupLog()
const settings = createSettingsServiceFromEnv(startupLog)
const userMapper = createUserMapperFromEnv(startupLog, settings)
Expand All @@ -49,6 +51,7 @@ const createServicesFromEnv = (): Services => {
userMapper,
})
return {
workflow,
userMapper,
categories,
settings,
Expand All @@ -60,6 +63,7 @@ const createServicesFromEnv = (): Services => {
profiles,
notifications,
jobs: createJobExecutorServiceFromEnv({
workflow,
syslog,
notifications,
adverts,
Expand Down
7 changes: 7 additions & 0 deletions src/test-utils/test-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ export const createTestNotificationServices = (
})

export const createTestServices = (services: Partial<Services>): Services => {
const workflow = {
get pickOnCollect() {
return false
},
}
const settings = services.settings || createInMemorySettingsService()
const userMapper = services.userMapper || createUserMapper(null, settings)
const categories = services.categories || categoryAdapter(settings)
Expand All @@ -135,6 +140,7 @@ export const createTestServices = (services: Partial<Services>): Services => {
const subscriptions = createNullSubscriptionsRepository()

return {
workflow,
userMapper,
categories,
settings,
Expand All @@ -146,6 +152,7 @@ export const createTestServices = (services: Partial<Services>): Services => {
files,
notifications,
jobs: createJobExecutorServiceFromEnv({
workflow,
syslog,
notifications,
adverts,
Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { EventLogService } from './events/types'
import type { SubscriptionsRepository } from './subscriptions/types'
import type { ContentRepository } from './content/types'
import type { SyslogService } from './syslog/types'
import type { WorkflowService } from './workflow/types'

export interface StartupLog {
echo: <TService>(
Expand All @@ -24,6 +25,7 @@ export interface StartupLog {
}

export interface Services {
workflow: WorkflowService
userMapper: UserMapper
categories: CategoryRepository
settings: SettingsService
Expand Down
8 changes: 8 additions & 0 deletions src/workflow/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { getEnv } from '../lib/gdi-api-node'
import type { WorkflowService } from './types'

export const createWorkflowServiceFromEnv = (): WorkflowService => ({
get pickOnCollect() {
return Number(getEnv('PICK_ON_COLLECT', { fallback: '0' })) === 1
},
})
3 changes: 3 additions & 0 deletions src/workflow/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface WorkflowService {
pickOnCollect: boolean
}

0 comments on commit 2f224a7

Please sign in to comment.