5dc29141d5
Adds a new Federation Graph page to display subgraphs and their schemas. Implements loading state and error handling. Enhances coverage reporting by including 'lcov' format for better insights into test coverage metrics.
226 lines
5.9 KiB
Vue
226 lines
5.9 KiB
Vue
<template>
|
|
<div>
|
|
<v-row>
|
|
<v-col cols="12">
|
|
<v-btn
|
|
prepend-icon="mdi-arrow-left"
|
|
variant="text"
|
|
@click="navigateTo('/schemas')"
|
|
>
|
|
Back to Schemas
|
|
</v-btn>
|
|
</v-col>
|
|
</v-row>
|
|
|
|
<v-row v-if="schema">
|
|
<v-col cols="12">
|
|
<h1 class="text-h3 mb-2">{{ schema.service }}</h1>
|
|
<v-chip class="mr-2">{{ schema.ref }}</v-chip>
|
|
<v-chip color="success">Active</v-chip>
|
|
</v-col>
|
|
</v-row>
|
|
|
|
<v-row v-if="schema">
|
|
<v-col cols="12" md="6">
|
|
<v-card>
|
|
<v-card-title>Details</v-card-title>
|
|
<v-card-text>
|
|
<v-list>
|
|
<v-list-item>
|
|
<v-list-item-title>Service</v-list-item-title>
|
|
<v-list-item-subtitle>{{ schema.service }}</v-list-item-subtitle>
|
|
</v-list-item>
|
|
<v-list-item>
|
|
<v-list-item-title>Ref</v-list-item-title>
|
|
<v-list-item-subtitle>{{ schema.ref }}</v-list-item-subtitle>
|
|
</v-list-item>
|
|
<v-list-item>
|
|
<v-list-item-title>URL</v-list-item-title>
|
|
<v-list-item-subtitle>{{ schema.url }}</v-list-item-subtitle>
|
|
</v-list-item>
|
|
<v-list-item v-if="schema.wsUrl">
|
|
<v-list-item-title>WebSocket URL</v-list-item-title>
|
|
<v-list-item-subtitle>{{ schema.wsUrl }}</v-list-item-subtitle>
|
|
</v-list-item>
|
|
<v-list-item>
|
|
<v-list-item-title>Last Updated</v-list-item-title>
|
|
<v-list-item-subtitle>{{ schema.updatedAt }}</v-list-item-subtitle>
|
|
</v-list-item>
|
|
<v-list-item>
|
|
<v-list-item-title>Updated By</v-list-item-title>
|
|
<v-list-item-subtitle>{{ schema.updatedBy }}</v-list-item-subtitle>
|
|
</v-list-item>
|
|
</v-list>
|
|
</v-card-text>
|
|
</v-card>
|
|
</v-col>
|
|
|
|
<v-col cols="12" md="6">
|
|
<v-card>
|
|
<v-card-title>Actions</v-card-title>
|
|
<v-card-text>
|
|
<v-btn
|
|
block
|
|
prepend-icon="mdi-download"
|
|
variant="outlined"
|
|
class="mb-2"
|
|
@click="downloadSchema"
|
|
>
|
|
Download SDL
|
|
</v-btn>
|
|
<v-btn
|
|
block
|
|
prepend-icon="mdi-content-copy"
|
|
variant="outlined"
|
|
class="mb-2"
|
|
@click="copyToClipboard"
|
|
>
|
|
Copy SDL
|
|
</v-btn>
|
|
<v-btn
|
|
block
|
|
prepend-icon="mdi-graph"
|
|
variant="outlined"
|
|
color="primary"
|
|
@click="viewFederationGraph"
|
|
>
|
|
View Federation Graph
|
|
</v-btn>
|
|
</v-card-text>
|
|
</v-card>
|
|
</v-col>
|
|
</v-row>
|
|
|
|
<v-row v-if="schema">
|
|
<v-col cols="12">
|
|
<v-card>
|
|
<v-card-title class="d-flex justify-space-between align-center">
|
|
<span>Schema Definition (SDL)</span>
|
|
<v-btn
|
|
icon="mdi-content-copy"
|
|
variant="text"
|
|
size="small"
|
|
@click="copyToClipboard"
|
|
/>
|
|
</v-card-title>
|
|
<v-card-text>
|
|
<pre class="sdl-viewer"><code>{{ schema.sdl }}</code></pre>
|
|
</v-card-text>
|
|
</v-card>
|
|
</v-col>
|
|
</v-row>
|
|
|
|
<v-row v-if="!schema && !loading">
|
|
<v-col cols="12" class="text-center py-12">
|
|
<v-icon icon="mdi-alert-circle" size="64" class="mb-4 text-warning" />
|
|
<p class="text-h6">Schema not found</p>
|
|
</v-col>
|
|
</v-row>
|
|
|
|
<v-snackbar v-model="snackbar" :timeout="2000">
|
|
{{ snackbarText }}
|
|
</v-snackbar>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
const route = useRoute()
|
|
const _config = useRuntimeConfig()
|
|
|
|
const schema = ref<any>(null)
|
|
const loading = ref(true)
|
|
const snackbar = ref(false)
|
|
const snackbarText = ref('')
|
|
|
|
const downloadSchema = () => {
|
|
if (!schema.value) return
|
|
|
|
const blob = new Blob([schema.value.sdl], { type: 'text/plain' })
|
|
const url = URL.createObjectURL(blob)
|
|
const a = document.createElement('a')
|
|
a.href = url
|
|
a.download = `${schema.value.service}-${schema.value.ref}.graphql`
|
|
a.click()
|
|
URL.revokeObjectURL(url)
|
|
|
|
snackbarText.value = 'Schema downloaded'
|
|
snackbar.value = true
|
|
}
|
|
|
|
const copyToClipboard = async () => {
|
|
if (!schema.value) return
|
|
|
|
try {
|
|
await navigator.clipboard.writeText(schema.value.sdl)
|
|
snackbarText.value = 'SDL copied to clipboard'
|
|
snackbar.value = true
|
|
} catch (_err) {
|
|
snackbarText.value = 'Failed to copy'
|
|
snackbar.value = true
|
|
}
|
|
}
|
|
|
|
const viewFederationGraph = () => {
|
|
if (!schema.value?.ref) return
|
|
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>
|
|
|
|
<style scoped>
|
|
.sdl-viewer {
|
|
background: #f5f5f5;
|
|
padding: 16px;
|
|
border-radius: 4px;
|
|
overflow-x: auto;
|
|
font-family: Monaco, Menlo, 'Ubuntu Mono', monospace;
|
|
font-size: 14px;
|
|
line-height: 1.5;
|
|
}
|
|
</style>
|