Skip to content

Commit

Permalink
feat: read current api usecase with fake promise data
Browse files Browse the repository at this point in the history
  • Loading branch information
ChengShi-1 committed Oct 16, 2024
1 parent ff8ed38 commit d85c671
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 9 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
},
"dependencies": {
"@faker-js/faker": "7.6.0",
"@iqss/dataverse-client-javascript": "2.0.0-pr192.6406015",
"@iqss/dataverse-client-javascript": "2.0.0-pr199.da60ecd",
"@iqss/dataverse-design-system": "*",
"@istanbuljs/nyc-config-typescript": "1.0.2",
"@tanstack/react-table": "8.9.2",
Expand Down
4 changes: 4 additions & 0 deletions src/api-token-info/domain/models/TokenInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface TokenInfo {
apiToken: string
expirationDate: string
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { TokenInfo } from '../models/TokenInfo'

export interface ApiTokenInfoRepository {
getCurrentApiToken(): Promise<TokenInfo>
recreateApiToken(): Promise<TokenInfo>
deleteApiToken(): Promise<void>
}
8 changes: 8 additions & 0 deletions src/api-token-info/domain/useCases/readCurrentApiToken.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { TokenInfo } from '../models/TokenInfo'
import { ApiTokenInfoRepository } from '../repositories/ApiTokenInfoRepository'

export function readCurrentApiToken(
apiTokenRepository: ApiTokenInfoRepository
): Promise<TokenInfo> {
return apiTokenRepository.getCurrentApiToken()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// import { ReadError } from '@iqss/dataverse-client-javascript'
// import {
// getCurrentApiToken,
// recreateCurrentApiToken,
// deleteCurrentApiToken,
// ApiTokenInfo
// } from '@iqss/dataverse-client-javascript'

import { TokenInfo } from '../domain/models/TokenInfo'
import { ApiTokenInfoRepository } from '../domain/repositories/ApiTokenInfoRepository'

export class ApiTokenInfoJSDataverseRepository implements ApiTokenInfoRepository {
getCurrentApiToken(): Promise<TokenInfo> {
return Promise.resolve({
apiToken: '142354345435eefrr',
expirationDate: new Date().toISOString().substring(0, 10)
})
}
recreateApiToken(): Promise<TokenInfo> {
return Promise.resolve({
apiToken: 'apiToken',
expirationDate: new Date().toISOString().substring(0, 10)
})
}

deleteApiToken(): Promise<void> {
return Promise.resolve()
}
// getCurrentApiToken(): Promise<TokenInfo> {
// return execute()
// .then((apiTokenInfo: TokenInfo) => {
// return {
// apiToken: apiTokenInfo.apiToken,
// expirationDate: apiTokenInfo.expirationDate
// }
// })
// .catch((error: ReadError) => {
// throw new Error(error.message)
// })
// }
// recreateApiToken(): Promise<TokenInfo> {
// return recreateCurrentApiToken
// .execute()
// .then((apiTokenInfo: ApiTokenInfo) => {
// return {
// apiToken: apiTokenInfo.apiToken,
// expirationDate: apiTokenInfo.expirationDate
// }
// })
// .catch((error: ReadError) => {
// throw new Error(error.message)
// })
// }
// deleteApiToken(): Promise<void> {
// return deleteCurrentApiToken()}
}
56 changes: 52 additions & 4 deletions src/sections/account/api-token-section/ApiTokenSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,36 @@ import { Trans, useTranslation } from 'react-i18next'
import { Button } from '@iqss/dataverse-design-system'
import accountStyles from '../Account.module.scss'
import styles from './ApiTokenSection.module.scss'

import { useEffect, useMemo, useState } from 'react'
import { TokenInfo } from '@/api-token-info/domain/models/TokenInfo'
import { readCurrentApiToken } from '@/api-token-info/domain/useCases/readCurrentApiToken'
import { ApiTokenInfoJSDataverseRepository } from '@/api-token-info/infrastructure/ApiTokenInfoJSDataverseRepository'
import ApiTokenSectionSkeleton from './ApiTokenSectionSkeleton'
export const ApiTokenSection = () => {
const { t } = useTranslation('account', { keyPrefix: 'apiToken' })

// TODO: When we have the use cases we need to mock stub to unit test this with or without token
const apiToken = '999fff-666rrr-this-is-not-a-real-token-123456'
const expirationDate = '2025-09-04'
const [apiToken, setApiToken] = useState<TokenInfo['apiToken']>('')
const [expirationDate, setExpirationDate] = useState<TokenInfo['expirationDate']>('')
const [loading, setLoading] = useState(true)
const repository = useMemo(() => new ApiTokenInfoJSDataverseRepository(), [])

useEffect(() => {
setLoading(true)
readCurrentApiToken(repository)
.then((tokenInfo) => {
if (tokenInfo) {
setApiToken(tokenInfo.apiToken)
setExpirationDate(tokenInfo.expirationDate)
}
})
.catch((error) => {
console.error('There was an error fetching current Api token:', error)
})
.finally(() => {
setLoading(false)
})
}, [repository])

const copyToClipboard = () => {
navigator.clipboard.writeText(apiToken).catch(
Expand All @@ -17,6 +40,28 @@ export const ApiTokenSection = () => {
}
)
}
if (loading) {
return (
<>
<p className={accountStyles['helper-text']}>
<Trans
t={t}
i18nKey="helperText"
components={{
anchor: (
<a
href="http://guides.dataverse.org/en/latest/api"
target="_blank"
rel="noreferrer"
/>
)
}}
/>
</p>
<ApiTokenSectionSkeleton data-testid="loadingSkeleton" />
</>
)
}

return (
<>
Expand All @@ -38,7 +83,10 @@ export const ApiTokenSection = () => {
{apiToken ? (
<>
<p className={styles['exp-date']}>
{t('expirationDate')} <time dateTime={expirationDate}>{expirationDate}</time>
{t('expirationDate')}{' '}
<time data-testid="expiration-date" dateTime={expirationDate}>
{expirationDate}
</time>
</p>
<div className={styles['api-token']}>
<code data-testid="api-token">{apiToken}</code>
Expand Down
37 changes: 37 additions & 0 deletions src/sections/account/api-token-section/ApiTokenSectionSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton'
import 'react-loading-skeleton/dist/skeleton.css'
import { Button } from '@iqss/dataverse-design-system'
import styles from './ApiTokenSection.module.scss'
import { useTranslation } from 'react-i18next'

const ApiTokenSectionSkeleton = () => {
const { t } = useTranslation('account', { keyPrefix: 'apiToken' })

return (
<SkeletonTheme>
<>
<p className={styles['exp-date']}>
{t('expirationDate')}{' '}
<time data-testid="expiration-date">
<Skeleton width={100} />
</time>
</p>
<div className={styles['api-token']}>
<code data-testid="api-token">
<Skeleton />
</code>
</div>
<div className={styles['btns-wrapper']} role="group">
<Button variant="secondary">{t('copyToClipboard')}</Button>
<Button variant="secondary" disabled>
{t('recreateToken')}
</Button>
<Button variant="secondary" disabled>
{t('revokeToken')}
</Button>
</div>
</>
</SkeletonTheme>
)
}
export default ApiTokenSectionSkeleton
28 changes: 28 additions & 0 deletions tests/component/sections/account/ApiTokenSection.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ApiTokenSection } from '../../../../src/sections/account/api-token-section/ApiTokenSection'
import { ApiTokenInfoJSDataverseRepository } from '../../../../src/api-token-info/infrastructure/ApiTokenInfoJSDataverseRepository'

describe('ApiTokenSection', () => {
beforeEach(() => {
Expand All @@ -21,4 +22,31 @@ describe('ApiTokenSection', () => {
})

// TODO: When we get the api token from the use case, we could mock the response and test more things.
describe('when fetching the current API token', () => {
it('should fetch and display the current API token', () => {
const mockApiToken = 'mocked-api-token'
const mockExpirationDate = '2024-12-31'

//TODO: we need change the fake call to the real one once we have the api working
cy.stub(ApiTokenInfoJSDataverseRepository.prototype, 'getCurrentApiToken').callsFake(() =>
Promise.resolve({
apiToken: mockApiToken,
expirationDate: mockExpirationDate
})
)
cy.mountAuthenticated(<ApiTokenSection />)

cy.get('[data-testid="api-token"]').should('contain.text', mockApiToken)
cy.get('[data-testid="expiration-date"]').should('contain.text', mockExpirationDate)
})
it('should display skeleton when the API token is fetching', () => {
it('should display skeleton when the API token is fetching', () => {
cy.stub(ApiTokenInfoJSDataverseRepository.prototype, 'getCurrentApiToken').callsFake(
() => new Promise(() => {})
)

cy.get('[data-testid="loadingSkeleton"]').should('exist')
})
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ApiTokenInfoJSDataverseRepository } from '../../../../../../src/api-token-info/infrastructure/ApiTokenInfoJSDataverseRepository'
import { TokenInfo } from '../../../../../../src/api-token-info/domain/models/TokenInfo'
//TODO: we need to change the fake call to the real one once we have the api working
const apiTokenRepository = new ApiTokenInfoJSDataverseRepository()
const tokenInfoExpected: TokenInfo = {
apiToken: '142354345435eefrr',
expirationDate: '2024-10-16'
}

describe('get api Token Repository', () => {
it('should return the current api token', async () => {
const tokenInfo = await apiTokenRepository.getCurrentApiToken()
expect(tokenInfo).to.deep.equal(tokenInfoExpected)
})

it('should return a new api token', async () => {
const tokenInfo = await apiTokenRepository.recreateApiToken()
expect(tokenInfo).to.deep.equal(tokenInfo)
})

it('should delete the current api token', async () => {
await apiTokenRepository.deleteApiToken()
})
})

0 comments on commit d85c671

Please sign in to comment.