Skip to content
This repository has been archived by the owner on Jan 3, 2025. It is now read-only.

Commit

Permalink
[Monolith Integration]: Update to align with current implementation (#…
Browse files Browse the repository at this point in the history
…263)

* Introduce route methods and removed payment mocks

* Use wcif for schedule and events

* remove accidental import of process

* added route for payment finish

* Update Frontend/package.json

Co-authored-by: Kevin Matthews <49137025+kr-matthews@users.noreply.github.com>

* Update Frontend/src/pages/register/components/StripeWrapper.jsx

Co-authored-by: Kevin Matthews <49137025+kr-matthews@users.noreply.github.com>

* Update Frontend/src/pages/register/components/StripeWrapper.jsx

Co-authored-by: Kevin Matthews <49137025+kr-matthews@users.noreply.github.com>

* Update Frontend/src/pages/schedule/index.jsx

Co-authored-by: Kevin Matthews <49137025+kr-matthews@users.noreply.github.com>

* review changes

* Get Swagger up to date and introduce open-api fetch in the frontend

* fix missing js changes in schedule and events

* changed HTTP Versb to be capital as per new openapi-fetch version

* fix API contract for getSingleRegistration

* fix API contract for getAllRegistrations

* fix API contract for updateRegistration

* fix API Contract for updateRegistrations

* linting

* fixed tests

* fix rubocop

* Add error message if events or schedule fails to load

---------

Co-authored-by: Kevin Matthews <49137025+kr-matthews@users.noreply.github.com>
  • Loading branch information
FinnIckler and kr-matthews authored Oct 17, 2023
1 parent 30fa22b commit 1c4f4db
Show file tree
Hide file tree
Showing 41 changed files with 758 additions and 516 deletions.
4 changes: 2 additions & 2 deletions Frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"scripts": {
"build:types": "TYPES_ONLY=true node src/build.mjs",
"build:dev": "NODE_ENV=\"development\" ASSET_PATH=\"/dist\" API_URL=\"http://localhost:3001/api/v1\" AUTH_URL=\"http://localhost:3001/jwt\" node src/build.mjs",
"build:staging": "POLL_URL=\"https://1rq8d7dif3.execute-api.us-west-2.amazonaws.com/v1/staging\" NODE_ENV=\"staging\" ASSET_PATH=\"https://d1qizdh27al0a7.cloudfront.net/staging/dist\" API_URL=\"https://staging.registration.worldcubeassociation.org/api/v1\" AUTH_URL=\"https://staging.registration.worldcubeassociation.org/jwt\" node src/build.mjs",
"build:prod": "POLL_URL=\"https://1rq8d7dif3.execute-api.us-west-2.amazonaws.com/v1/prod\" NODE_ENV=\"production\" ASSET_PATH=\"https://d1qizdh27al0a7.cloudfront.net/dist\" API_URL=\"https://registration.worldcubeassociation.org/api/v1\" AUTH_URL=\"https://test-registration.worldcubeassociation.org/api/v10/auth/jwt\" node src/build.mjs",
"build:staging": "POLL_URL=\"https://1rq8d7dif3.execute-api.us-west-2.amazonaws.com/v1/staging\" NODE_ENV=\"staging\" ASSET_PATH=\"https://d1qizdh27al0a7.cloudfront.net/staging/dist\" API_URL=\"https://staging.registration.worldcubeassociation.org/api/v1\" WCA_URL=\"https://staging.worldcubeassociation.org\" node src/build.mjs",
"build:prod": "POLL_URL=\"https://1rq8d7dif3.execute-api.us-west-2.amazonaws.com/v1/prod\" NODE_ENV=\"production\" ASSET_PATH=\"https://d1qizdh27al0a7.cloudfront.net/dist\" API_URL=\"https://registration.worldcubeassociation.org/api/v1\" WCA_URL=\"https://worldcubeassociation.org\" node src/build.mjs",
"watch": "node src/watch.mjs",
"lint": "eslint src --ext .js,.jsx,.ts,.tsx",
"lint:fix": "eslint src --ext .js,.jsx,.ts,.tsx --fix"
Expand Down
5 changes: 2 additions & 3 deletions Frontend/src/api/auth/get_jwt.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { JWT_KEY } from '../../ui/providers/UserProvider'
import { BackendError } from '../helper/backend_fetch'
import { tokenRoute } from '../helper/routes'
import getJWTMock from '../mocks/get_jwt'

export async function getJWT(reauthenticate = false): Promise<string> {
Expand All @@ -9,9 +10,7 @@ export async function getJWT(reauthenticate = false): Promise<string> {
// the jwt token is cached in local storage, if it has expired, we need to reauthenticate
const cachedToken = localStorage.getItem(JWT_KEY)
if (reauthenticate || cachedToken === null) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore AUTH_URL is injected at build time
const response = await fetch(process.env.AUTH_URL)
const response = await fetch(tokenRoute)
const body = await response.json()
if (response.ok) {
const token = response.headers.get('authorization')
Expand Down
7 changes: 3 additions & 4 deletions Frontend/src/api/auth/get_permissions.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import externalServiceFetch from '../helper/external_service_fetch'
import { permissionsRoute } from '../helper/routes'
import getPermissionsMock from '../mocks/get_permissions'

export interface Permissions {
can_attend_competitions: { scope: Scope; reasons?: number }
can_attend_competitions: { scope: Scope; until?: string }
can_organize_competitions: { scope: Scope }
can_administer_competitions: { scope: Scope }
}
type Scope = '*' | string[]

export async function getPermissions(): Promise<Permissions> {
if (process.env.NODE_ENV === 'production') {
return externalServiceFetch(
'https://test-registration.worldcubeassociation.org/api/v10/users/me/permissions'
)
return externalServiceFetch(permissionsRoute)
}
return getPermissionsMock()
}
Expand Down
5 changes: 2 additions & 3 deletions Frontend/src/api/competition/get/get_competition_info.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import externalServiceFetch from '../../helper/external_service_fetch'
import { competitionInfoRoute } from '../../helper/routes'
import { CompetitionInfo } from '../../types'

export default async function getCompetitionInfo(
competitionId: string
): Promise<CompetitionInfo> {
return externalServiceFetch(
`https://test-registration.worldcubeassociation.org/api/v10/competitions/${competitionId}`
)
return externalServiceFetch(competitionInfoRoute(competitionId))
}
9 changes: 9 additions & 0 deletions Frontend/src/api/competition/get/get_competition_wcif.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Competition } from '@wca/helpers'
import externalServiceFetch from '../../helper/external_service_fetch'
import { competitionWCIFRoute } from '../../helper/routes'

export default async function getCompetitionWcif(
competitionId: string
): Promise<Competition> {
return externalServiceFetch(competitionWCIFRoute(competitionId))
}
19 changes: 10 additions & 9 deletions Frontend/src/api/helper/error_codes.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
// see app/helpers/error_codes.rb
// TODO generate these errors out of some kind of shared error file
export const INVALID_TOKEN = -1
export const EXPIRED_TOKEN = -2
export const MISSING_AUTHENTICATION = -3
export const SERVICE_DOWN = -4

export const COMPETITION_NOT_FOUND = -1000
export const COMPETITION_API_5XX = -1001
export const COMPETITION_CLOSED = -1002
export const COMPETITION_INVALID_EVENTS = -1003

export const USER_IMPERSONATION = -2000
export const USER_IS_BANNED = -2001
export const USER_PROFILE_INCOMPLETE = -2002
export const USER_INSUFFICIENT_PERMISSIONS = -2003
export const USER_NOT_LOGGED_IN = -2004

export const REGISTRATION_NOT_FOUND = -3000
export const PAYMENT_NOT_ENABLED = -3001
export const PAYMENT_NOT_READY = -3002
export const INVALID_REQUEST_DATA = -4000
export const EVENT_EDIT_DEADLINE_PASSED = -4001
export const GUEST_LIMIT_EXCEEDED = -4002
export const USER_COMMENT_TOO_LONG = -4003
export const INVALID_EVENT_SELECTION = -4004
export const REQUIRED_COMMENT_MISSING = -4005
export const COMPETITOR_LIMIT_REACHED = -4006
export const INVALID_REGISTRATION_STATUS = -4007
export const REGISTRATION_CLOSED = -4008
7 changes: 5 additions & 2 deletions Frontend/src/api/helper/external_service_fetch.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { BackendError } from './backend_fetch'

export default async function externalServiceFetch(route: string) {
const response = await fetch(route)
export default async function externalServiceFetch(
route: string,
options = {}
) {
const response = await fetch(route, options)
const body = await response.json()
if (response.ok) {
return body
Expand Down
21 changes: 21 additions & 0 deletions Frontend/src/api/helper/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export const tokenRoute = `${process.env.WCA_URL}/api/v0/users/token`
export const permissionsRoute = `${process.env.WCA_URL}/api/v0/users/me/permissions`
export const paymentConfigRoute = `${process.env.WCA_URL}/payment/config`
export const paymentFinishRoute = (competitionId: string, userId: string) =>
`${process.env.WCA_URL}/payment/${competitionId}-${userId}/finish`
// TODO: Move this to swagger once finalised
export const paymentIdRoute = (id: string) =>
`${process.env.API_URL}/${id}/payment`
export const meRoute = `${process.env.WCA_URL}/api/v0/users/me`
// This will break when urls get really big, maybe we should switch to POST?
export const usersInfoRoute = (ids: string[]) =>
`${process.env.WCA_URL}/api/v0/users?${ids
.map((id) => 'ids[]=' + id)
.join('&')}`
// Hardcoded because these are currently not mocked
export const competitionInfoRoute = (id: string) =>
`https://api.worldcubeassociation.org/competitions/${id}`
export const competitionWCIFRoute = (id: string) =>
`https://api.worldcubeassociation.org/competitions/${id}/wcif/public`
export const userInfoRoute = (id: string) =>
`https://api.worldcubeassociation.org/users/${id}`
21 changes: 0 additions & 21 deletions Frontend/src/api/mocks/get_payment_intent.ts

This file was deleted.

3 changes: 0 additions & 3 deletions Frontend/src/api/mocks/get_permissions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { USER_KEY } from '../../ui/providers/UserProvider'
import { Permissions } from '../auth/get_permissions'
import { USER_IS_BANNED, USER_PROFILE_INCOMPLETE } from '../helper/error_codes'

export default function getPermissionsMock(): Permissions {
const userId = localStorage.getItem(USER_KEY)
Expand Down Expand Up @@ -57,7 +56,6 @@ export default function getPermissionsMock(): Permissions {
return {
can_attend_competitions: {
scope: [],
reasons: USER_IS_BANNED,
},
can_organize_competitions: {
scope: [],
Expand All @@ -70,7 +68,6 @@ export default function getPermissionsMock(): Permissions {
return {
can_attend_competitions: {
scope: [],
reasons: USER_PROFILE_INCOMPLETE,
},
can_organize_competitions: {
scope: [],
Expand Down
8 changes: 0 additions & 8 deletions Frontend/src/api/mocks/get_stripe_config.ts

This file was deleted.

22 changes: 12 additions & 10 deletions Frontend/src/api/payment/get/get_stripe_config.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import externalServiceFetch from '../../helper/external_service_fetch'
import getStripeConfigMock from '../../mocks/get_stripe_config'
import { paymentConfigRoute } from '../../helper/routes'

export interface StripeConfig {
stripe_publishable_key: string
connected_account_id: string
client_secret: string
}

export default async function getStripeConfig(
competitionId: string
competitionId: string,
paymentId: string
): Promise<StripeConfig> {
if (process.env.NODE_ENV === 'production') {
// This should live in the payment service?
return externalServiceFetch(
`https://test-registration.worldcubeassociation.org/api/v10/payment/${competitionId}/config`
)
}

return getStripeConfigMock()
return externalServiceFetch(paymentConfigRoute, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: {
competitionId,
paymentId,
},
})
}
18 changes: 8 additions & 10 deletions Frontend/src/api/registration/get/get_payment_intent.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import backendFetch from '../../helper/backend_fetch'
import getPaymentIntentMock from '../../mocks/get_payment_intent'
import { paymentIdRoute } from '../../helper/routes'

export interface PaymentInfo {
client_secret_id: string
// This is the MySQL payment id that can be give to the payment service
// to get the relevant data, not the Stripe ID!
payment_id: string
}
// We get the user_id out of the JWT key, which is why we only send the
// competition_id
export default async function getPaymentIntent(
export default async function getPaymentId(
competitionId: string
): Promise<PaymentInfo> {
if (process.env.NODE_ENV === 'production') {
// This should live in the payment service?
return backendFetch(`/${competitionId}/payment`, 'GET', {
needsAuthentication: true,
}) as Promise<PaymentInfo>
}
return getPaymentIntentMock()
return backendFetch(paymentIdRoute(competitionId), 'GET', {
needsAuthentication: true,
}) as Promise<PaymentInfo>
}
75 changes: 33 additions & 42 deletions Frontend/src/api/registration/get/get_registrations.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,36 @@
import { EventId } from '@wca/helpers'
import createClient from 'openapi-fetch'
import { getJWT } from '../../auth/get_jwt'
import backendFetch, { BackendError } from '../../helper/backend_fetch'
import { paths } from '../../schema'
import getCompetitorInfo, { User } from '../../user/get/get_user_info'
import { EXPIRED_TOKEN } from '../../helper/error_codes'
import { components, paths } from '../../schema'
import getCompetitorInfo from '../../user/get/get_user_info'

const { get } = createClient<paths>({
const { GET } = createClient<paths>({
// TODO: Change this once we are fully migrated from backend fetch
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
baseUrl: process.env.API_URL.slice(0, -7),
})

type RegistrationStatus = 'waiting' | 'accepted' | 'deleted'
type PaymentStatus = 'initialized' | 'waiting' | 'completed'

interface Registration {
user_id: string
user: User
event_ids: EventId[]
}

export interface RegistrationAdmin {
user_id: string
event_ids: EventId[]
registration_status: RegistrationStatus
payment_status: PaymentStatus
registered_on: string
comment: string
admin_comment: string
guests: number
user: User
}

export async function getConfirmedRegistrations(
competitionID: string
): Promise<Registration[]> {
): Promise<components['schemas']['registration'][]> {
//TODO: Because there is currently no bulk user fetch route we need to manually add user data here
const { data, error, response } = await get(
const { data, response } = await GET(
'/api/v1/registrations/{competition_id}',
{
params: { path: { competition_id: competitionID } },
}
)
const regList = []
if (error) {
throw new BackendError(error.error, response.status)
if (!response.ok) {
throw new BackendError(500, response.status)
}
for (const registration of data) {
for (const registration of data!) {
const user = (await getCompetitorInfo(registration.user_id)).user
regList.push({
user_id: registration.user_id,
event_ids: registration.event_ids,
competing: registration.competing,
user,
})
}
Expand All @@ -59,30 +39,41 @@ export async function getConfirmedRegistrations(

export async function getAllRegistrations(
competitionID: string
): Promise<RegistrationAdmin[]> {
): Promise<components['schemas']['registrationAdmin'][]> {
//TODO: Because there is currently no bulk user fetch route we need to manually add user data here
const registrations = (await backendFetch(
`/registrations/${competitionID}/admin`,
'GET',
const { data, error, response } = await GET(
'/api/v1/registrations/{competition_id}/admin',
{
needsAuthentication: true,
params: { path: { competition_id: competitionID } },
headers: { Authorization: await getJWT() },
}
)) as RegistrationAdmin[]
)
const regList = []
for (const registration of registrations) {
registration.user = (await getCompetitorInfo(registration.user_id)).user
regList.push(registration)
if (error) {
if (error.error === EXPIRED_TOKEN) {
await getJWT(true)
return getAllRegistrations(competitionID)
}
throw new BackendError(error.error, response.status)
}
for (const registration of data!) {
const user = (await getCompetitorInfo(registration.user_id)).user
regList.push({
user_id: registration.user_id,
competing: registration.competing,
user,
})
}
return regList
}

export async function getSingleRegistration(
userId: string,
competitionId: string
): Promise<{ registration: RegistrationAdmin }> {
): Promise<{ registration: components['schemas']['registrationAdmin'] }> {
return backendFetch(
`/register?user_id=${userId}&competition_id=${competitionId}`,
'GET',
{ needsAuthentication: true }
) as Promise<{ registration: RegistrationAdmin }>
) as Promise<{ registration: components['schemas']['registrationAdmin'] }>
}
Loading

0 comments on commit 1c4f4db

Please sign in to comment.