Skip to content

Commit b994225

Browse files
authored
Merge pull request #193 from IQSS/190-api-tokens
Api token management use cases
2 parents e246194 + 8ac7738 commit b994225

20 files changed

+487
-27
lines changed

docs/useCases.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ The different use cases currently available in the package are classified below,
5353
- [Users](#Users)
5454
- [Users read use cases](#users-read-use-cases)
5555
- [Get Current Authenticated User](#get-current-authenticated-user)
56+
- [Get Current API Token](#get-current-api-token)
57+
- [Users write use cases](#users-write-use-cases)
58+
- [Delete Current API Token](#delete-current-api-token)
59+
- [Recreate Current API Token](#recreate-current-api-token)
5660
- [Info](#Info)
5761
- [Get Dataverse Backend Version](#get-dataverse-backend-version)
5862
- [Get Maximum Embargo Duration In Months](#get-maximum-embargo-duration-in-months)
@@ -1172,6 +1176,66 @@ getCurrentAuthenticatedUser.execute().then((user: AuthenticatedUser) => {
11721176

11731177
_See [use case](../src/users/domain/useCases/GetCurrentAuthenticatedUser.ts) implementation_.
11741178

1179+
### Get Current API Token
1180+
1181+
Returns the current [ApiTokenInfo](../src/users/domain/models/ApiTokenInfo.ts) corresponding to the current authenticated user.
1182+
1183+
##### Example call:
1184+
1185+
```typescript
1186+
import { getCurrentApiToken } from '@iqss/dataverse-client-javascript'
1187+
1188+
/* ... */
1189+
1190+
getCurrentApiToken.execute().then((apiTokenInfo: ApiTokenInfo) => {
1191+
/* ... */
1192+
})
1193+
1194+
/* ... */
1195+
```
1196+
1197+
_See [use case](../src/users/domain/useCases/GetCurrentApiToken.ts) implementation_.
1198+
1199+
### Users write use cases
1200+
1201+
### Delete Current API Token
1202+
1203+
Deletes the API token of the current authenticated user.
1204+
1205+
##### Example call:
1206+
1207+
```typescript
1208+
import { deleteCurrentApiToken } from '@iqss/dataverse-client-javascript'
1209+
1210+
/* ... */
1211+
1212+
deleteCurrentApiToken.execute()
1213+
1214+
/* ... */
1215+
```
1216+
1217+
_See [use case](../src/users/domain/useCases/DeleteCurrentApiToken.ts) implementation_.
1218+
1219+
### Recreate Current API Token
1220+
1221+
Reacreates the API token of the current authenticated user and returns the new [ApiTokenInfo](../src/users/domain/models/ApiTokenInfo.ts).
1222+
1223+
##### Example call:
1224+
1225+
```typescript
1226+
import { recreateCurrentApiToken } from '@iqss/dataverse-client-javascript'
1227+
1228+
/* ... */
1229+
1230+
recreateCurrentApiToken.execute().then((apiTokenInfo: ApiTokenInfo) => {
1231+
/* ... */
1232+
})
1233+
1234+
/* ... */
1235+
```
1236+
1237+
_See [use case](../src/users/domain/useCases/RecreateCurrentApiToken.ts) implementation_.
1238+
11751239
## Info
11761240

11771241
#### Get Dataverse Backend Version

src/core/infra/repositories/ApiRepository.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ export abstract class ApiRepository {
3535
return await this.doRequest('put', apiEndpoint, data, queryParams)
3636
}
3737

38+
public async doDelete(apiEndpoint: string, queryParams: object = {}): Promise<AxiosResponse> {
39+
return await axios
40+
.delete(buildRequestUrl(apiEndpoint), buildRequestConfig(true, queryParams))
41+
.then((response) => response)
42+
.catch((error) => {
43+
throw new WriteError(this.buildErrorMessage(error))
44+
})
45+
}
46+
3847
protected buildApiEndpoint(
3948
resourceName: string,
4049
operation: string,
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface ApiTokenInfo {
2+
apiToken: string
3+
expirationDate: Date
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
import { ApiTokenInfo } from '../models/ApiTokenInfo'
12
import { AuthenticatedUser } from '../models/AuthenticatedUser'
23

34
export interface IUsersRepository {
45
getCurrentAuthenticatedUser(): Promise<AuthenticatedUser>
6+
recreateCurrentApiToken(): Promise<ApiTokenInfo>
7+
getCurrentApiToken(): Promise<ApiTokenInfo>
8+
deleteCurrentApiToken(): Promise<void>
59
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { UseCase } from '../../../core/domain/useCases/UseCase'
2+
import { IUsersRepository } from '../repositories/IUsersRepository'
3+
4+
export class DeleteCurrentApiToken implements UseCase<void> {
5+
private usersRepository: IUsersRepository
6+
7+
constructor(usersRepository: IUsersRepository) {
8+
this.usersRepository = usersRepository
9+
}
10+
11+
/**
12+
* Deletes the API token of the current authenticated user.
13+
*
14+
* @returns {Promise<void>}
15+
*/
16+
async execute(): Promise<void> {
17+
return await this.usersRepository.deleteCurrentApiToken()
18+
}
19+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { UseCase } from '../../../core/domain/useCases/UseCase'
2+
import { ApiTokenInfo } from '../models/ApiTokenInfo'
3+
import { IUsersRepository } from '../repositories/IUsersRepository'
4+
5+
export class GetCurrentApiToken implements UseCase<ApiTokenInfo> {
6+
private usersRepository: IUsersRepository
7+
8+
constructor(usersRepository: IUsersRepository) {
9+
this.usersRepository = usersRepository
10+
}
11+
12+
/**
13+
* Returns the current ApiTokenInfo corresponding to the current authenticated user.
14+
*
15+
* @returns {Promise<ApiTokenInfo>}
16+
*/
17+
async execute(): Promise<ApiTokenInfo> {
18+
return await this.usersRepository.getCurrentApiToken()
19+
}
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { UseCase } from '../../../core/domain/useCases/UseCase'
2+
import { ApiTokenInfo } from '../models/ApiTokenInfo'
3+
import { IUsersRepository } from '../repositories/IUsersRepository'
4+
5+
export class RecreateCurrentApiToken implements UseCase<ApiTokenInfo> {
6+
private usersRepository: IUsersRepository
7+
8+
constructor(usersRepository: IUsersRepository) {
9+
this.usersRepository = usersRepository
10+
}
11+
12+
/**
13+
* Reacreates the API token of the current authenticated user and returns the new ApiTokenInfo.
14+
*
15+
* @returns {Promise<ApiTokenInfo>}
16+
*/
17+
async execute(): Promise<ApiTokenInfo> {
18+
return await this.usersRepository.recreateCurrentApiToken()
19+
}
20+
}

src/users/index.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
11
import { UsersRepository } from './infra/repositories/UsersRepository'
22
import { GetCurrentAuthenticatedUser } from './domain/useCases/GetCurrentAuthenticatedUser'
3+
import { RecreateCurrentApiToken } from './domain/useCases/RecreateCurrentApiToken'
4+
import { GetCurrentApiToken } from './domain/useCases/GetCurrentApiToken'
5+
import { DeleteCurrentApiToken } from './domain/useCases/DeleteCurrentApiToken'
36

4-
const getCurrentAuthenticatedUser = new GetCurrentAuthenticatedUser(new UsersRepository())
7+
const usersRepository = new UsersRepository()
58

6-
export { getCurrentAuthenticatedUser }
9+
const getCurrentAuthenticatedUser = new GetCurrentAuthenticatedUser(usersRepository)
10+
const recreateCurrentApiToken = new RecreateCurrentApiToken(usersRepository)
11+
const getCurrentApiToken = new GetCurrentApiToken(usersRepository)
12+
const deleteCurrentApiToken = new DeleteCurrentApiToken(usersRepository)
13+
14+
export {
15+
getCurrentAuthenticatedUser,
16+
recreateCurrentApiToken,
17+
getCurrentApiToken,
18+
deleteCurrentApiToken
19+
}
720
export { AuthenticatedUser } from './domain/models/AuthenticatedUser'
21+
export { ApiTokenInfo } from './domain/models/ApiTokenInfo'

src/users/infra/repositories/UsersRepository.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,47 @@ import { ApiRepository } from '../../../core/infra/repositories/ApiRepository'
22
import { IUsersRepository } from '../../domain/repositories/IUsersRepository'
33
import { AuthenticatedUser } from '../../domain/models/AuthenticatedUser'
44
import { AxiosResponse } from 'axios'
5+
import { ApiTokenInfo } from '../../domain/models/ApiTokenInfo'
6+
import {
7+
transformGetApiTokenResponseToApiTokenInfo,
8+
transformRecreateApiTokenResponseToApiTokenInfo
9+
} from './transformers/apiTokenInfoTransformers'
510

611
export class UsersRepository extends ApiRepository implements IUsersRepository {
12+
private readonly usersResourceName: string = 'users'
13+
714
public async getCurrentAuthenticatedUser(): Promise<AuthenticatedUser> {
8-
return this.doGet('/users/:me', true)
15+
return this.doGet(`/${this.usersResourceName}/:me`, true)
916
.then((response) => this.getAuthenticatedUserFromResponse(response))
1017
.catch((error) => {
1118
throw error
1219
})
1320
}
1421

22+
public async recreateCurrentApiToken(): Promise<ApiTokenInfo> {
23+
return this.doPost(`/${this.usersResourceName}/token/recreate?returnExpiration=true`, {})
24+
.then((response) => transformRecreateApiTokenResponseToApiTokenInfo(response))
25+
.catch((error) => {
26+
throw error
27+
})
28+
}
29+
30+
public async getCurrentApiToken(): Promise<ApiTokenInfo> {
31+
return this.doGet(`/${this.usersResourceName}/token`, true)
32+
.then((response) => transformGetApiTokenResponseToApiTokenInfo(response))
33+
.catch((error) => {
34+
throw error
35+
})
36+
}
37+
38+
public async deleteCurrentApiToken(): Promise<void> {
39+
return this.doDelete(`/${this.usersResourceName}/token`)
40+
.then(() => undefined)
41+
.catch((error) => {
42+
throw error
43+
})
44+
}
45+
1546
private getAuthenticatedUserFromResponse(response: AxiosResponse): AuthenticatedUser {
1647
const responseData = response.data.data
1748
return {
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { AxiosResponse } from 'axios'
2+
import { ApiTokenInfo } from '../../../domain/models/ApiTokenInfo'
3+
4+
export const transformGetApiTokenResponseToApiTokenInfo = (
5+
response: AxiosResponse
6+
): ApiTokenInfo => {
7+
const apiTokenInfoPayload = response.data.data
8+
const messageParts = apiTokenInfoPayload.message.split(' ')
9+
const expirationDateFormattedTimestamp = `${messageParts[4]}T${messageParts[5]}`
10+
return {
11+
apiToken: messageParts[1],
12+
expirationDate: new Date(expirationDateFormattedTimestamp)
13+
}
14+
}
15+
16+
export const transformRecreateApiTokenResponseToApiTokenInfo = (
17+
response: AxiosResponse
18+
): ApiTokenInfo => {
19+
const apiTokenInfoPayload = response.data.data
20+
const messageParts = apiTokenInfoPayload.message.split(' ')
21+
const expirationDateFormattedTimestamp = `${messageParts[9]}T${messageParts[10]}`
22+
return {
23+
apiToken: messageParts[5],
24+
expirationDate: new Date(expirationDateFormattedTimestamp)
25+
}
26+
}

0 commit comments

Comments
 (0)