Skip to content

Commit

Permalink
feat(2915): review feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
koekiebox committed Feb 12, 2025
1 parent 719bb18 commit e718b64
Show file tree
Hide file tree
Showing 13 changed files with 47 additions and 93 deletions.
3 changes: 0 additions & 3 deletions localenv/mock-account-servicing-entity/generated/graphql.ts

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

16 changes: 0 additions & 16 deletions packages/backend/src/graphql/generated/graphql.schema.json

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

3 changes: 0 additions & 3 deletions packages/backend/src/graphql/generated/graphql.ts

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

4 changes: 1 addition & 3 deletions packages/backend/src/graphql/resolvers/tenant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { GraphQLErrorCode } from '../errors'
import { Tenant } from '../../tenants/model'
import { Pagination, SortOrder } from '../../shared/baseModel'
import { getPageInfo } from '../../shared/pagination'
import { Config } from '../../config/app'

export const whoami: QueryResolvers<TenantedApolloContext>['whoami'] = async (
parent,
Expand Down Expand Up @@ -175,7 +174,6 @@ export function tenantToGraphQl(tenant: Tenant): SchemaTenant {
createdAt: new Date(+tenant.createdAt).toISOString(),
deletedAt: tenant.deletedAt
? new Date(+tenant.deletedAt).toISOString()
: null,
isOperator: tenant.apiSecret === Config.adminApiSecret
: null
}
}
2 changes: 0 additions & 2 deletions packages/backend/src/graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1551,8 +1551,6 @@ type Tenant implements Model {
createdAt: String!
"The date and time that this tenant was deleted."
deletedAt: String
"Is the tenant an Operator tenant."
isOperator: Boolean!
}

type TenantsConnection {
Expand Down
7 changes: 2 additions & 5 deletions packages/frontend/app/generated/graphql.ts

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

2 changes: 0 additions & 2 deletions packages/frontend/app/lib/api/tenant.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ export const listTenants = async (request: Request, args: QueryTenantsArgs) => {
publicName
createdAt
deletedAt
isOperator
}
}
pageInfo {
Expand Down Expand Up @@ -161,7 +160,6 @@ export const getTenantInfo = async (
publicName
createdAt
deletedAt
isOperator
}
}
`,
Expand Down
15 changes: 3 additions & 12 deletions packages/frontend/app/lib/validate.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,19 +132,10 @@ export const updateTenantSchema = z
.object({
apiSecret: z
.string()
.min(10, { message: 'API Secret should be at least 3 characters long' })
.max(255, { message: 'Maximum length of API Secret is 255 characters' })
.regex(
/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/,
{ message: 'API Secret should be Base64 encoded.' }
),
.min(10, { message: 'API Secret should be at least 10 characters long' })
.max(255, { message: 'Maximum length of API Secret is 255 characters' }),
publicName: z.string().optional(),
email: z
.string()
.regex(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/, {
message: 'Invalid email address.'
})
.optional(),
email: z.string().optional(),
idpConsentUrl: z.string().optional(),
idpSecret: z.string().optional()
})
Expand Down
39 changes: 14 additions & 25 deletions packages/frontend/app/routes/tenants.$tenantId.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
await checkAuthAndRedirect(request.url, cookies)

const tenantId = params.tenantId

const result = z.string().uuid().safeParse(tenantId)
if (!result.success) {
throw json(null, { status: 400, statusText: 'Invalid tenant ID.' })
Expand All @@ -42,13 +41,11 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
throw json(null, { status: 404, statusText: 'Tenant not found.' })

const me = await whoAmI(request)
const isOperator = me.isOperator

return json({ tenant, isOperator })
return json({ tenant, me })
}

export default function ViewTenantPage() {
const { tenant, isOperator } = useLoaderData<typeof loader>()
const { tenant, me } = useLoaderData<typeof loader>()
const response = useActionData<typeof action>()
const navigation = useNavigation()
const [formData, setFormData] = useState<FormData>()
Expand Down Expand Up @@ -98,12 +95,6 @@ export default function ViewTenantPage() {
disabled
readOnly
/>
<Input
label='Is Operator'
value={tenant.isOperator ? 'Yes' : 'No'}
disabled
readOnly
/>
<Input
label='Public Name'
name='publicName'
Expand Down Expand Up @@ -147,6 +138,7 @@ export default function ViewTenantPage() {
label='API Secret'
defaultValue={tenant.apiSecret ?? undefined}
required
disabled
/>
</div>
<div className='flex justify-end p-4'>
Expand Down Expand Up @@ -205,20 +197,17 @@ export default function ViewTenantPage() {
{/* Identity Provider Information - END */}

{/* DELETE TENANT - Danger zone */}
<DangerZone title='Delete Tenant'>
<Form method='post' onSubmit={submitHandler}>
<Input type='hidden' name='id' value={tenant.id} />
<Input type='hidden' name='intent' value='delete' />
<Button
disabled={!isOperator}
type='submit'
intent='danger'
aria-label='delete tenant'
>
Delete tenant
</Button>
</Form>
</DangerZone>
{me.isOperator && me.id !== tenant.id && (
<DangerZone title='Delete Tenant'>
<Form method='post' onSubmit={submitHandler}>
<Input type='hidden' name='id' value={tenant.id} />
<Input type='hidden' name='intent' value='delete' />
<Button type='submit' intent='danger' aria-label='delete tenant'>
Delete tenant
</Button>
</Form>
</DangerZone>
)}
</div>
<ConfirmationDialog
ref={dialogRef}
Expand Down
15 changes: 5 additions & 10 deletions packages/frontend/app/routes/tenants._index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,13 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
tenantPageInfo,
previousPageUrl,
nextPageUrl,
isOperator
me
})
}

export default function TenantsPage() {
const {
tenantEdges,
tenantPageInfo,
previousPageUrl,
nextPageUrl,
isOperator
} = useLoaderData<typeof loader>()
const { tenantEdges, tenantPageInfo, previousPageUrl, nextPageUrl, me } =
useLoaderData<typeof loader>()
const navigate = useNavigate()

return (
Expand All @@ -71,7 +66,7 @@ export default function TenantsPage() {
<h3 className='text-2xl'>Tenants</h3>
</div>
<div className='ml-auto'>
{isOperator && (
{me.isOperator && (
<Button aria-label='add new tenant' to='/tenants/create'>
Add tenant
</Button>
Expand Down Expand Up @@ -106,7 +101,7 @@ export default function TenantsPage() {
</span>
)}
</span>
{tenant.node.isOperator && (
{me.isOperator && me.id == tenant.node.id && (
<Badge color={BadgeColor.Yellow}>Operator</Badge>
)}
</div>
Expand Down
28 changes: 22 additions & 6 deletions packages/frontend/app/routes/tenants.create.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { json, type ActionFunctionArgs } from '@remix-run/node'
import { Form, useActionData, useNavigation } from '@remix-run/react'
import { json, type ActionFunctionArgs, redirect } from '@remix-run/node'
import {
Form,
useActionData,
useLoaderData,
useNavigation
} from '@remix-run/react'
import { PageHeader } from '~/components'
import { Button, ErrorPanel, Input, PasswordInput } from '~/components/ui'
import { createTenant } from '~/lib/api/tenant.server'
import { createTenant, whoAmI } from '~/lib/api/tenant.server'
import { messageStorage, setMessageAndRedirect } from '~/lib/message.server'
import { createTenantSchema } from '~/lib/validate.server'
import type { ZodFieldErrors } from '~/shared/types'
Expand All @@ -12,13 +17,16 @@ import { type LoaderFunctionArgs } from '@remix-run/node'
export const loader = async ({ request }: LoaderFunctionArgs) => {
const cookies = request.headers.get('cookie')
await checkAuthAndRedirect(request.url, cookies)
return null
const me = await whoAmI(request)
return json({ me })
}

export default function CreateTenantPage() {
const response = useActionData<typeof action>()
const { state } = useNavigation()
const isSubmitting = state === 'submitting'
const { me } = useLoaderData<typeof loader>()
if (!me || !me.isOperator) throw redirect('tenants')

return (
<div className='pt-4 flex flex-col space-y-4'>
Expand Down Expand Up @@ -126,14 +134,22 @@ export async function action({ request }: ActionFunctionArgs) {

const formData = Object.fromEntries(await request.formData())
const result = createTenantSchema.safeParse(formData)

if (!result.success) {
errors.fieldErrors = result.error.flatten().fieldErrors
return json({ errors }, { status: 400 })
}
if (
result.data.email &&
result.data.email.trim().length > 0 &&
!new RegExp(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/).test(
result.data.email
)
) {
errors.message = ['Email is invalid.']
return json({ errors }, { status: 400 })
}

const response = await createTenant(request, { ...result.data })

if (!response?.tenant) {
errors.message = ['Could not create tenant. Please try again!']
return json({ errors }, { status: 400 })
Expand Down
3 changes: 0 additions & 3 deletions packages/mock-account-service-lib/src/generated/graphql.ts

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

3 changes: 0 additions & 3 deletions test/integration/lib/generated/graphql.ts

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

0 comments on commit e718b64

Please sign in to comment.