feat: enhance authentication error handling and schema fetching #13
+48
-48
@@ -124,14 +124,60 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useAuth0 } from '@auth0/auth0-vue'
|
||||||
|
|
||||||
|
import { useLatestSchemaQuery } from '~/graphql/generated'
|
||||||
|
|
||||||
|
const auth0 = useAuth0()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const _config = useRuntimeConfig()
|
const _config = useRuntimeConfig()
|
||||||
|
|
||||||
const schema = ref<any>(null)
|
// Parse the ID which should contain both ref and service info
|
||||||
const loading = ref(true)
|
// The ID format from backend is typically: orgId_ref_service or similar
|
||||||
|
const schemaId = route.params.id as string
|
||||||
|
|
||||||
const snackbar = ref(false)
|
const snackbar = ref(false)
|
||||||
const snackbarText = ref('')
|
const snackbarText = ref('')
|
||||||
|
|
||||||
|
// We need to fetch all schemas and find the matching one
|
||||||
|
// Since we don't have a direct query for a single subgraph by ID,
|
||||||
|
// we'll need to get it from the URL or store it in a better format
|
||||||
|
// For now, let's extract ref from the route query or use a default
|
||||||
|
const schemaRef = computed(() => {
|
||||||
|
return (route.query.ref as string) || 'production'
|
||||||
|
})
|
||||||
|
|
||||||
|
// Fetch latest schema for the ref
|
||||||
|
const latestSchemaQuery = useLatestSchemaQuery(() => ({
|
||||||
|
ref: schemaRef.value,
|
||||||
|
}), () => ({
|
||||||
|
skip: !auth0.isAuthenticated.value,
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Find the specific subgraph matching our ID
|
||||||
|
const schema = computed(() => {
|
||||||
|
if (!latestSchemaQuery.result.value?.latestSchema?.subGraphs) return null
|
||||||
|
|
||||||
|
const subgraph = latestSchemaQuery.result.value.latestSchema.subGraphs.find(
|
||||||
|
s => s.id === schemaId,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!subgraph) return null
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: subgraph.id,
|
||||||
|
service: subgraph.service,
|
||||||
|
ref: schemaRef.value,
|
||||||
|
url: subgraph.url || 'N/A',
|
||||||
|
wsUrl: subgraph.wsUrl,
|
||||||
|
updatedAt: new Date(subgraph.changedAt).toLocaleString(),
|
||||||
|
updatedBy: subgraph.changedBy,
|
||||||
|
sdl: subgraph.sdl,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = computed(() => latestSchemaQuery.loading.value)
|
||||||
|
|
||||||
const downloadSchema = () => {
|
const downloadSchema = () => {
|
||||||
if (!schema.value) return
|
if (!schema.value) return
|
||||||
|
|
||||||
@@ -164,52 +210,6 @@ const viewFederationGraph = () => {
|
|||||||
if (!schema.value?.ref) return
|
if (!schema.value?.ref) return
|
||||||
navigateTo(`/graph/${schema.value.ref}`)
|
navigateTo(`/graph/${schema.value.ref}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Fetch actual data from the GraphQL API
|
|
||||||
onMounted(() => {
|
|
||||||
// Mock data for now
|
|
||||||
setTimeout(() => {
|
|
||||||
schema.value = {
|
|
||||||
id: route.params.id,
|
|
||||||
service: 'users-service',
|
|
||||||
ref: 'production',
|
|
||||||
url: 'http://users.example.com/graphql',
|
|
||||||
wsUrl: 'ws://users.example.com/graphql',
|
|
||||||
updatedAt: '2024-11-21 19:30:00',
|
|
||||||
updatedBy: 'john.doe@example.com',
|
|
||||||
sdl: `type User @key(fields: "id") {
|
|
||||||
id: ID!
|
|
||||||
username: String!
|
|
||||||
email: String!
|
|
||||||
createdAt: DateTime!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Query {
|
|
||||||
user(id: ID!): User
|
|
||||||
users(limit: Int, offset: Int): [User!]!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Mutation {
|
|
||||||
createUser(input: CreateUserInput!): User!
|
|
||||||
updateUser(id: ID!, input: UpdateUserInput!): User!
|
|
||||||
deleteUser(id: ID!): Boolean!
|
|
||||||
}
|
|
||||||
|
|
||||||
input CreateUserInput {
|
|
||||||
username: String!
|
|
||||||
email: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input UpdateUserInput {
|
|
||||||
username: String
|
|
||||||
email: String
|
|
||||||
}
|
|
||||||
|
|
||||||
scalar DateTime`,
|
|
||||||
}
|
|
||||||
loading.value = false
|
|
||||||
}, 500)
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
md="6"
|
md="6"
|
||||||
lg="4"
|
lg="4"
|
||||||
>
|
>
|
||||||
<v-card hover @click="navigateTo(`/schemas/${schema.id}`)">
|
<v-card hover @click="navigateTo(`/schemas/${schema.id}?ref=${schema.ref}`)">
|
||||||
<v-card-title>
|
<v-card-title>
|
||||||
<v-icon icon="mdi-graphql" class="mr-2" />
|
<v-icon icon="mdi-graphql" class="mr-2" />
|
||||||
{{ schema.service }}
|
{{ schema.service }}
|
||||||
|
|||||||
@@ -51,8 +51,24 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error('[Apollo] Failed to get Auth0 token:', error)
|
// Handle consent required error
|
||||||
|
if (error?.error === 'consent_required') {
|
||||||
|
console.warn('[Apollo] Consent required, redirecting to login...')
|
||||||
|
const auth0Instance = (nuxtApp.vueApp.config.globalProperties as any).$auth0
|
||||||
|
// Trigger login with consent
|
||||||
|
await auth0Instance.loginWithRedirect({
|
||||||
|
authorizationParams: {
|
||||||
|
prompt: 'consent',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else if (error?.error === 'login_required') {
|
||||||
|
console.warn('[Apollo] Login required, redirecting...')
|
||||||
|
const auth0Instance = (nuxtApp.vueApp.config.globalProperties as any).$auth0
|
||||||
|
await auth0Instance.loginWithRedirect()
|
||||||
|
} else {
|
||||||
|
console.error('[Apollo] Failed to get Auth0 token:', error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { headers }
|
return { headers }
|
||||||
|
|||||||
Reference in New Issue
Block a user