1
0
mirror of https://github.com/oceanprotocol/market.git synced 2024-12-02 05:57:29 +01:00

Handle provider error & showing Retry button (#1770)

* adding error handling for button onCLick

* Updating butoon to show retry & updating toast message

* Logging provider error

* Removing unused imports

* Using early return when provider fees are present to prevent unneccessary nesting

* Adding retry logic for compute

* Removing duplicate teast message

* Adding tests for rendering Buy button

* Additional tests for BuyButton

* Refactoring BuyButton tests for download

* Fix failing tests

* Refactoring compute tests

* Refactoring tests

Co-authored-by: Matthias Kretschmann <m@kretschmann.io>
This commit is contained in:
Jamie Hewitt 2022-11-11 12:54:21 +03:00 committed by GitHub
parent 4b4a926f89
commit 4ff84b0871
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 207 additions and 38 deletions

View File

@ -8,7 +8,8 @@ import {
OrderParams,
ProviderComputeInitialize,
ProviderFees,
ProviderInstance
ProviderInstance,
ProviderInitialize
} from '@oceanprotocol/lib'
import Web3 from 'web3'
import { getOceanConfig } from './ocean'
@ -20,6 +21,26 @@ import {
} from '../../app.config'
import { toast } from 'react-toastify'
async function initializeProvider(
asset: AssetExtended,
accountId: string,
providerFees?: ProviderFees
): Promise<ProviderInitialize> {
if (providerFees) return
try {
const provider = await ProviderInstance.initialize(
asset.id,
asset.services[0].id,
0,
accountId,
asset.services[0].serviceEndpoint
)
return provider
} catch (error) {
LoggerInstance.log('[Initialize Provider] Error:', error)
}
}
/**
* @param web3
* @param asset
@ -40,15 +61,11 @@ export async function order(
const datatoken = new Datatoken(web3)
const config = getOceanConfig(asset.chainId)
const initializeData =
!providerFees &&
(await ProviderInstance.initialize(
asset.id,
asset.services[0].id,
0,
accountId,
asset.services[0].serviceEndpoint
))
const initializeData = await initializeProvider(
asset,
accountId,
providerFees
)
const orderParams = {
consumer: computeConsumerAddress || accountId,
@ -130,15 +147,11 @@ export async function reuseOrder(
providerFees?: ProviderFees
): Promise<TransactionReceipt> {
const datatoken = new Datatoken(web3)
const initializeData =
!providerFees &&
(await ProviderInstance.initialize(
asset.id,
asset.services[0].id,
0,
accountId,
asset.services[0].serviceEndpoint
))
const initializeData = await initializeProvider(
asset,
accountId,
providerFees
)
const tx = await datatoken.reuseOrder(
asset.accessDetails.datatoken.address,

View File

@ -0,0 +1,143 @@
import React from 'react'
import { render, screen } from '@testing-library/react'
import ButtonBuy, { ButtonBuyProps } from './'
const downloadProps: ButtonBuyProps = {
action: 'download',
disabled: false,
hasPreviousOrder: false,
hasDatatoken: false,
btSymbol: 'btSymbol',
dtSymbol: 'dtSymbol',
dtBalance: '100000000000',
assetTimeout: '1 day',
assetType: 'Dataset',
stepText: 'TEST',
priceType: 'fixed',
isConsumable: true,
isBalanceSufficient: true,
consumableFeedback: 'TEST: consumableFeedback'
}
const computeProps: ButtonBuyProps = {
action: 'compute',
disabled: false,
hasPreviousOrder: false,
hasDatatoken: true,
btSymbol: 'btSymbol',
dtSymbol: 'dtSymbol',
dtBalance: '100000000000',
assetTimeout: '1 day',
assetType: 'algorithm',
hasPreviousOrderSelectedComputeAsset: false,
hasDatatokenSelectedComputeAsset: true,
dtSymbolSelectedComputeAsset: 'dtSymbol',
dtBalanceSelectedComputeAsset: 'dtBalance',
selectedComputeAssetType: 'selectedComputeAssetType',
stepText: ' ',
isLoading: false,
type: 'submit',
priceType: 'fixed',
algorithmPriceType: 'free',
isBalanceSufficient: true,
isConsumable: true,
consumableFeedback: 'consumableFeedback',
isAlgorithmConsumable: true,
hasProviderFee: false,
retry: false
}
describe('Asset/AssetActions/ButtonBuy', () => {
// TESTS FOR LOADING
it('Renders Buy button without crashing', () => {
render(<ButtonBuy {...downloadProps} isLoading />)
const button = screen.getByText('TEST')
expect(button).toContainHTML('<Loader')
})
// TESTS FOR DOWNLOAD
it('Renders Buy button without crashing', () => {
render(<ButtonBuy {...downloadProps} />)
const button = screen.getByText('Buy for 1 day')
expect(button).toContainHTML('<button')
})
it('Renders Buy button without crashing when hasPreviousOrder=true', () => {
render(<ButtonBuy {...downloadProps} hasPreviousOrder />)
const button = screen.getByText('Download')
expect(button).toContainHTML('<button')
})
it('Renders retry button for download without crashing', () => {
render(<ButtonBuy {...downloadProps} retry />)
const button = screen.getByText('Retry')
expect(button).toContainHTML('<button')
})
it('Renders get button for free download without crashing', () => {
render(<ButtonBuy {...downloadProps} priceType="free" hasPreviousOrder />)
const button = screen.getByText('Download')
expect(button).toContainHTML('<button')
})
it('Renders "Get" button for free assets without crashing', () => {
render(<ButtonBuy {...downloadProps} priceType="free" />)
const button = screen.getByText('Get')
expect(button).toContainHTML('<button')
})
it('Renders Buy button without crashing', () => {
render(
<ButtonBuy
{...downloadProps}
assetTimeout="Forever"
isConsumable={false}
/>
)
const button = screen.getByText('Buy')
expect(button).toContainHTML('<button')
})
// TESTS FOR COMPUTE
it('Renders "Buy Compute Job" button for compute without crashing', () => {
render(<ButtonBuy {...computeProps} />)
const button = screen.getByText('Buy Compute Job')
expect(button).toContainHTML('<button')
})
it('Renders "Buy Compute Job" button for compute without crashing', () => {
render(<ButtonBuy {...computeProps} hasDatatokenSelectedComputeAsset />)
const button = screen.getByText('Buy Compute Job')
expect(button).toContainHTML('<button')
})
it('Renders "Start Compute Job" button', () => {
render(
<ButtonBuy
{...computeProps}
hasPreviousOrder
hasPreviousOrderSelectedComputeAsset
/>
)
const button = screen.getByText('Start Compute Job')
expect(button).toContainHTML('<button')
})
it('Renders "Order Compute Job" button', () => {
render(<ButtonBuy {...computeProps} priceType="free" hasProviderFee />)
const button = screen.getByText('Order Compute Job')
expect(button).toContainHTML('<button')
})
it('Renders "Order Compute Job" button', () => {
render(<ButtonBuy {...computeProps} priceType="free" hasProviderFee />)
const button = screen.getByText('Order Compute Job')
expect(button).toContainHTML('<button')
})
it('Renders "retry" button for compute without crashing', () => {
render(<ButtonBuy {...computeProps} retry />)
const button = screen.getByText('Retry')
expect(button).toContainHTML('<button')
})
})

View File

@ -3,7 +3,7 @@ import Button from '../../../@shared/atoms/Button'
import styles from './index.module.css'
import Loader from '../../../@shared/atoms/Loader'
interface ButtonBuyProps {
export interface ButtonBuyProps {
action: 'download' | 'compute'
disabled: boolean
hasPreviousOrder: boolean
@ -29,6 +29,7 @@ interface ButtonBuyProps {
algorithmPriceType?: string
isAlgorithmConsumable?: boolean
hasProviderFee?: boolean
retry?: boolean
}
// TODO: we need to take a look at these messages
@ -131,22 +132,24 @@ export default function ButtonBuy({
priceType,
algorithmPriceType,
isAlgorithmConsumable,
hasProviderFee
hasProviderFee,
retry
}: ButtonBuyProps): ReactElement {
const buttonText =
action === 'download'
? hasPreviousOrder
? 'Download'
: priceType === 'free'
? 'Get'
: `Buy ${assetTimeout === 'Forever' ? '' : ` for ${assetTimeout}`}`
: hasPreviousOrder &&
hasPreviousOrderSelectedComputeAsset &&
!hasProviderFee
? 'Start Compute Job'
: priceType === 'free' && algorithmPriceType === 'free'
? 'Order Compute Job'
: `Buy Compute Job`
const buttonText = retry
? 'Retry'
: action === 'download'
? hasPreviousOrder
? 'Download'
: priceType === 'free'
? 'Get'
: `Buy ${assetTimeout === 'Forever' ? '' : ` for ${assetTimeout}`}`
: hasPreviousOrder &&
hasPreviousOrderSelectedComputeAsset &&
!hasProviderFee
? 'Start Compute Job'
: priceType === 'free' && algorithmPriceType === 'free'
? 'Order Compute Job'
: `Buy Compute Job`
return (
<div className={styles.actions}>

View File

@ -43,7 +43,8 @@ export default function FormStartCompute({
datasetOrderPriceAndFees,
algoOrderPriceAndFees,
providerFeeAmount,
validUntil
validUntil,
retry
}: {
algorithms: AssetSelectionAsset[]
ddoListAlgorithms: Asset[]
@ -71,6 +72,7 @@ export default function FormStartCompute({
algoOrderPriceAndFees?: OrderPriceAndFees
providerFeeAmount?: string
validUntil?: string
retry: boolean
}): ReactElement {
const { siteContent } = useMarketMetadata()
const { accountId, balance } = useWeb3()
@ -294,6 +296,7 @@ export default function FormStartCompute({
selectedAlgorithmAsset?.accessDetails?.isPurchasable
}
hasProviderFee={providerFeeAmount && providerFeeAmount !== '0'}
retry={retry}
/>
</Form>
)

View File

@ -97,6 +97,7 @@ export default function Compute({
const [refetchJobs, setRefetchJobs] = useState(false)
const [isLoadingJobs, setIsLoadingJobs] = useState(false)
const [jobs, setJobs] = useState<ComputeJobMetaData[]>([])
const [retry, setRetry] = useState<boolean>(false)
const hasDatatoken = Number(dtBalance) >= 1
const isComputeButtonDisabled =
@ -291,7 +292,8 @@ export default function Compute({
useEffect(() => {
const newError = error
if (!newError) return
toast.error(newError)
const errorMsg = newError + '. Please retry.'
toast.error(errorMsg)
}, [error])
async function startJob(): Promise<void> {
@ -386,6 +388,7 @@ export default function Compute({
initPriceAndFees()
} catch (error) {
setError(error.message)
setRetry(true)
LoggerInstance.error(`[compute] ${error.message} `)
} finally {
setIsOrdering(false)
@ -477,6 +480,7 @@ export default function Compute({
algoOrderPriceAndFees={algoOrderPriceAndFees}
providerFeeAmount={providerFeeAmount}
validUntil={computeValidUntil}
retry={retry}
/>
</Formik>
)}

View File

@ -48,6 +48,7 @@ export default function Download({
const [isOrderDisabled, setIsOrderDisabled] = useState(false)
const [orderPriceAndFees, setOrderPriceAndFees] =
useState<OrderPriceAndFees>()
const [retry, setRetry] = useState<boolean>(false)
const isUnsupportedPricing = asset?.accessDetails?.type === 'NOT_SUPPORTED'
@ -155,9 +156,10 @@ export default function Download({
}
} catch (error) {
LoggerInstance.error(error)
setRetry(true)
const message = isOwned
? 'Failed to download file!'
: 'An error occurred. Check console for more information.'
: 'An error occurred, please retry. Check console for more information.'
toast.error(message)
}
setIsLoading(false)
@ -181,6 +183,7 @@ export default function Download({
isConsumable={asset.accessDetails?.isPurchasable}
isBalanceSufficient={isBalanceSufficient}
consumableFeedback={consumableFeedback}
retry={retry}
/>
)