Skip to content

Commit

Permalink
🔥 Hotfix: Unresponsive Proxy API (#4842)
Browse files Browse the repository at this point in the history
* Do not share a cold observable between queries
e.g all `api.derive` were sharing the same cold observable.
Which resulted in all other payloads being deserialize on every emitted message (from other `api.derive` queries).
Same thing for `api.query` and `api.rpc`.

* Revert "Debug failing test"

This reverts commit 7337a7d.

* Fix the multiple recipient funding request proposal

* Bump version to `3.5.1`
  • Loading branch information
thesan committed Apr 25, 2024
1 parent 6254af1 commit 0e563b4
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 134 deletions.
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [3.5.1] - 2024-04-25

## [3.5.0 (Luxor)][3.5.0] - 2024-03-18
### Fixed
- Proxy API becoming unresponsive.

## [3.5.0 (Luxor)][3.5.0] - 2024-04-18

### Added
- Decrease council budget proposal.
Expand Down Expand Up @@ -380,7 +384,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [0.1.1] - 2022-12-02

[unreleased]: https://github.com/Joystream/pioneer/compare/v3.5.0...HEAD
[unreleased]: https://github.com/Joystream/pioneer/compare/v3.5.1...HEAD
[3.5.1]: https://github.com/Joystream/pioneer/compare/v3.5.0...v3.5.1
[3.5.0]: https://github.com/Joystream/pioneer/compare/v3.4.0...v3.5.0
[3.4.0]: https://github.com/Joystream/pioneer/compare/v3.3.1...v3.4.0
[3.3.1]: https://github.com/Joystream/pioneer/compare/v3.3.0...v3.3.1
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@joystream/pioneer",
"version": "3.5.0",
"version": "3.5.1",
"license": "GPL-3.0-only",
"scripts": {
"build": "node --max_old_space_size=4096 ./build.js",
Expand Down
184 changes: 92 additions & 92 deletions packages/ui/src/app/pages/Proposals/CurrentProposals.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -804,98 +804,98 @@ export const SpecificParametersFundingRequest: Story = {
}),
}

// export const SpecificParametersMultipleFundingRequest: Story = {
// play: specificParametersTest('Funding Request', async ({ args, createProposal, modal, step }) => {
// const aliceAddress = alice.controllerAccount
// const bobAddress = member('bob').controllerAccount
// const charlieAddress = member('charlie').controllerAccount

// await createProposal(async () => {
// const nextButton = getButtonByText(modal, 'Create proposal')
// expect(nextButton).toBeDisabled()

// await userEvent.click(modal.getByTestId('pay-multiple'))

// const csvField = modal.getByTestId('accounts-amounts')

// // Invalid
// await userEvent.clear(csvField)
// await userEvent.type(csvField, `${aliceAddress},500${bobAddress},500`)
// expect(await modal.findByText(/Not valid CSV format/))
// // ensure its not being open-able while the CSV syntax is valid
// const previewButton = getButtonByText(modal, 'Preview and Validate')
// expect(previewButton).toBeDisabled()
// await waitFor(() => expect(modal.queryByTestId('sidePanel-overlay')).toBeNull())
// expect(nextButton).toBeDisabled()

// // Invalid Accounts error
// await userEvent.clear(csvField)
// await userEvent.type(csvField, `5GNJqTPy,500\n${bobAddress},500`)

// await waitFor(() => expect(modal.queryByText(/Not valid CSV format/)).toBeNull())
// expect(await modal.findByText(/Please preview and validate the inputs to proceed/))
// expect(nextButton).toBeDisabled()
// expect(previewButton).toBeEnabled()

// await userEvent.click(previewButton)
// expect(await modal.findByText(/Incorrect destination accounts detected/))
// await userEvent.click(modal.getByTestId('sidePanel-overlay'))

// // Max Amount error
// await userEvent.clear(csvField)
// await userEvent.type(csvField, `${aliceAddress},166667\n${bobAddress},500`)
// expect(await modal.findByText(/Please preview and validate the inputs to proceed/))
// expect(nextButton).toBeDisabled()
// await waitFor(() => expect(previewButton).toBeEnabled())
// await waitFor(
// async () => {
// await userEvent.click(previewButton)
// expect(await modal.findByText(/Max payment amount is exceeded/))
// },
// { timeout: 8000 }
// )
// await userEvent.click(modal.getByTestId('sidePanel-overlay')) //ensure create proposal is still disabled
// expect(nextButton).toBeDisabled()

// // Max Allowed Accounts error
// await userEvent.clear(csvField)
// await userEvent.type(csvField, `${aliceAddress},400\n${bobAddress},500\n${charlieAddress},500`)
// expect(await modal.findByText(/Please preview and validate the inputs to proceed/))
// expect(nextButton).toBeDisabled()
// await waitFor(() => expect(previewButton).toBeEnabled())
// await userEvent.click(previewButton)
// expect(await modal.findByText(/Maximum allowed accounts exceeded/))
// await userEvent.click(modal.getByTestId('sidePanel-overlay')) //ensure create proposal is still disabled
// expect(nextButton).toBeDisabled()

// // delete one account from the list'
// await waitFor(() => expect(previewButton).toBeEnabled())
// await userEvent.click(previewButton)
// await userEvent.click(modal.getByTestId('removeAccount-2'))
// await waitFor(() => expect(modal.queryByText(/Maximum allowed accounts exceeded/)).toBeNull())
// await userEvent.click(modal.getByTestId('sidePanel-overlay'))

// // Valid
// await userEvent.clear(csvField)
// await userEvent.type(csvField, `${aliceAddress},500\n${bobAddress},500`)
// expect(nextButton).toBeDisabled()

// await waitFor(() => expect(previewButton).toBeEnabled())
// await userEvent.click(previewButton)
// await userEvent.click(modal.getByTestId('sidePanel-overlay'))
// })

// await step('Transaction parameters', () => {
// const [, , specificParameters] = args.onCreateProposal.mock.calls.at(-1)
// expect(specificParameters.toJSON()).toEqual({
// fundingRequest: [
// { account: aliceAddress, amount: 500_0000000000 },
// { account: bobAddress, amount: 500_0000000000 },
// ],
// })
// })
// }),
// }
export const SpecificParametersMultipleFundingRequest: Story = {
play: specificParametersTest('Funding Request', async ({ args, createProposal, modal, step }) => {
const aliceAddress = alice.controllerAccount
const bobAddress = member('bob').controllerAccount
const charlieAddress = member('charlie').controllerAccount

await createProposal(async () => {
const nextButton = getButtonByText(modal, 'Create proposal')
expect(nextButton).toBeDisabled()

await userEvent.click(modal.getByTestId('pay-multiple'))

const csvField = modal.getByTestId('accounts-amounts')

// Invalid
await userEvent.clear(csvField)
await userEvent.type(csvField, `${aliceAddress},500${bobAddress},500`)
expect(await modal.findByText(/Not valid CSV format/))
// ensure its not being open-able while the CSV syntax is valid
const previewButton = getButtonByText(modal, 'Preview and Validate')
expect(previewButton).toBeDisabled()
await waitFor(() => expect(modal.queryByTestId('sidePanel-overlay')).toBeNull())
expect(nextButton).toBeDisabled()

// Invalid Accounts error
await userEvent.clear(csvField)
await userEvent.type(csvField, `5GNJqTPy,500\n${bobAddress},500`)

await waitFor(() => expect(modal.queryByText(/Not valid CSV format/)).toBeNull())
expect(await modal.findByText(/Please preview and validate the inputs to proceed/))
expect(nextButton).toBeDisabled()
expect(previewButton).toBeEnabled()

await userEvent.click(previewButton)
expect(await modal.findByText(/Incorrect destination accounts detected/))
await userEvent.click(modal.getByTestId('sidePanel-overlay'))

// Max Amount error
await userEvent.clear(csvField)
await userEvent.type(csvField, `${aliceAddress},166667\n${bobAddress},500`)
expect(await modal.findByText(/Please preview and validate the inputs to proceed/))
expect(nextButton).toBeDisabled()
await waitFor(() => expect(previewButton).toBeEnabled())
await waitFor(
async () => {
await userEvent.click(previewButton)
expect(await modal.findByText(/Max payment amount is exceeded/))
},
{ timeout: 8000 }
)
await userEvent.click(modal.getByTestId('sidePanel-overlay')) //ensure create proposal is still disabled
expect(nextButton).toBeDisabled()

// Max Allowed Accounts error
await userEvent.clear(csvField)
await userEvent.type(csvField, `${aliceAddress},400\n${bobAddress},500\n${charlieAddress},500`)
expect(await modal.findByText(/Please preview and validate the inputs to proceed/))
expect(nextButton).toBeDisabled()
await waitFor(() => expect(previewButton).toBeEnabled())
await userEvent.click(previewButton)
expect(await modal.findByText(/Maximum allowed accounts exceeded/))
await userEvent.click(modal.getByTestId('sidePanel-overlay')) //ensure create proposal is still disabled
expect(nextButton).toBeDisabled()

// delete one account from the list'
await waitFor(() => expect(previewButton).toBeEnabled())
await userEvent.click(previewButton)
await userEvent.click(modal.getByTestId('removeAccount-2'))
await waitFor(() => expect(modal.queryByText(/Maximum allowed accounts exceeded/)).toBeNull())
await userEvent.click(modal.getByTestId('sidePanel-overlay'))

// Valid
await userEvent.clear(csvField)
await userEvent.type(csvField, `${aliceAddress},500\n${bobAddress},500`)
expect(nextButton).toBeDisabled()

await waitFor(() => expect(previewButton).toBeEnabled())
await userEvent.click(previewButton)
await userEvent.click(modal.getByTestId('sidePanel-overlay'))
})

await step('Transaction parameters', () => {
const [, , specificParameters] = args.onCreateProposal.mock.calls.at(-1)
expect(specificParameters.toJSON()).toEqual({
fundingRequest: [
{ account: aliceAddress, amount: 500_0000000000 },
{ account: bobAddress, amount: 500_0000000000 },
],
})
})
}),
}

export const SpecificParametersSetReferralCut: Story = {
play: specificParametersTest('Set Referral Cut', async ({ args, createProposal, modal, step }) => {
Expand Down
1 change: 0 additions & 1 deletion packages/ui/src/proposals/constants/regExp.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,36 @@ import { RowGapBlock } from '@/common/components/page/PageContent'
import { Tooltip, TooltipDefault } from '@/common/components/Tooltip'
import { TextMedium, TextSmall, TextInlineSmall } from '@/common/components/typography'
import { useResponsive } from '@/common/hooks/useResponsive'
import { isValidCSV } from '@/proposals/model/isValidCsv'

import { PreviewAndValidateModal } from './modals/PreviewAndValidate'
import { ErrorPrompt, Prompt } from './Prompt'

export const FundingRequest = () => {
const { watch, setValue, getFieldState } = useFormContext()
const { watch, setValue } = useFormContext()
const { isMobile, size } = useResponsive()
const [isPreviewModalShown, setIsPreviewModalShown] = useState(false)
const [payMultiple] = watch(['fundingRequest.payMultiple'])
const [hasPreviewedInput] = watch(['fundingRequest.hasPreviewedInput'], { 'fundingRequest.hasPreviewedInput': true })
const csvInput = watch('fundingRequest.csvInput')

const [hasPreviewedInput, setHasPreviewedInput] = useState(false)
const [canPreviewInput, setCanPreviewInput] = useState(false)

useEffect(() => {
if (getFieldState('fundingRequest.accountsAndAmounts')) {
setValue('fundingRequest.accountsAndAmounts', undefined, { shouldValidate: true })
setValue('fundingRequest.hasPreviewedInput', false, { shouldValidate: true })
}
}, [csvInput])
const canPreviewInput =
getFieldState('fundingRequest.csvInput').isDirty && !getFieldState('fundingRequest.csvInput').error
const subscription = watch((data, info) => {
if (info.name !== 'fundingRequest.csvInput' || info.type !== 'change') return

const isCsvInpuValid = isValidCSV(data.fundingRequest.csvInput)
setCanPreviewInput(isCsvInpuValid !== canPreviewInput)

if (hasPreviewedInput) {
setHasPreviewedInput(false)
setValue('fundingRequest.accountsAndAmounts', undefined, { shouldValidate: true })
}
})

return () => subscription.unsubscribe()
}, [watch])

return (
<RowGapBlock gap={24}>
<Row>
Expand Down Expand Up @@ -90,7 +101,8 @@ export const FundingRequest = () => {
<InputComponent
label="Destination accounts and transfer amounts"
required
message={'You can select up to 20 recipients'}
message={canPreviewInput ? 'You can select up to 20 recipients' : 'Not valid CSV format'}
validation={canPreviewInput ? undefined : 'invalid'}
name="fundingRequest.csvInput"
id="accounts-amounts"
inputSize="xl"
Expand Down
6 changes: 1 addition & 5 deletions packages/ui/src/proposals/modals/AddNewProposal/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
} from '@/common/utils/validation'
import { AccountSchema, StakingAccountSchema } from '@/memberships/model/validation'
import { Member } from '@/memberships/types'
import { equalToContext, isValidCSV } from '@/proposals/model/validation'
import { equalToContext } from '@/proposals/model/validation'
import { ProposalType } from '@/proposals/types'
import { GroupIdName } from '@/working-groups/types'

Expand Down Expand Up @@ -264,10 +264,6 @@ export const schemaFactory = (api?: Api) => {
.test('previewedinput', 'Please preview', (value) => typeof value !== 'undefined' && value)
.required('Field is required'),
}),
csvInput: Yup.string().when('payMultiple', {
is: true,
then: (schema) => schema.test(isValidCSV('Not valid CSV format')).required('Field is required'),
}),
accountsAndAmounts: Yup.array().when('payMultiple', {
is: true,
then: (schema) => schema.required(),
Expand Down
14 changes: 14 additions & 0 deletions packages/ui/src/proposals/model/isValidCsv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const CSV_PATTERN = /^([^,:;]+),([^,:;]+)(\n[^,:;]+,[^,:;]+)*(\n)?$/

export const isValidCSV = (value: string) => {
if (!CSV_PATTERN.test(value)) return false

const pairs = value.split('\n')

for (const pair of pairs) {
const [, amount] = pair.split(',')
if (!Number(amount)) return false
}

return true
}
20 changes: 0 additions & 20 deletions packages/ui/src/proposals/model/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,6 @@ import { get } from 'lodash'
import * as Yup from 'yup'
import { AnyObject } from 'yup/lib/types'

import { CSV_PATTERN } from '../constants/regExp'

export const isValidCSV = (message: string): Yup.TestConfig<any, AnyObject> => ({
message,
name: 'isValidCSV',
exclusive: false,
test(value: string) {
if (!CSV_PATTERN.test(value)) return false

const pairs = value.split('\n')

for (const pair of pairs) {
const [, amount] = pair.split(',')
if (!Number(amount)) return false
}

return true
},
})

export const equalToContext = (
msg: (value: any) => string,
contextPath: string,
Expand Down
5 changes: 3 additions & 2 deletions packages/ui/src/proxyApi/client/query.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AnyTuple } from '@polkadot/types/types'
import { uniqueId } from 'lodash'
import { filter, Observable, map } from 'rxjs'
import { filter, Observable, map, share } from 'rxjs'

import { deserializeMessage } from '../models/payload'
import { ApiKinds, PostMessage, RawWorkerMessageEvent } from '../types'
Expand Down Expand Up @@ -35,7 +35,8 @@ export const query = <K extends ApiQueryKinds>(
) => {
const queryMessages = messages.pipe(
filter(({ data }) => data.messageType === apiKind),
deserializeMessage<WorkerQueryMessage<K>>()
deserializeMessage<WorkerQueryMessage<K>>(),
share()
)

return apiInterfaceProxy<K>(
Expand Down

0 comments on commit 0e563b4

Please sign in to comment.