2025-11-22 16:42:35 +01:00
|
|
|
<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"
|
2025-11-22 19:49:53 +01:00
|
|
|
@click="viewFederationGraph"
|
2025-11-22 16:42:35 +01:00
|
|
|
>
|
|
|
|
|
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">
|
2025-11-23 14:01:32 +01:00
|
|
|
import { useAuth0 } from '@auth0/auth0-vue'
|
|
|
|
|
|
|
|
|
|
import { useLatestSchemaQuery } from '~/graphql/generated'
|
|
|
|
|
|
|
|
|
|
const auth0 = useAuth0()
|
2025-11-22 16:42:35 +01:00
|
|
|
const route = useRoute()
|
|
|
|
|
const _config = useRuntimeConfig()
|
|
|
|
|
|
2025-11-23 14:01:32 +01:00
|
|
|
// Parse the ID which should contain both ref and service info
|
|
|
|
|
// The ID format from backend is typically: orgId_ref_service or similar
|
|
|
|
|
const schemaId = route.params.id as string
|
|
|
|
|
|
2025-11-22 16:42:35 +01:00
|
|
|
const snackbar = ref(false)
|
|
|
|
|
const snackbarText = ref('')
|
|
|
|
|
|
2025-11-23 14:01:32 +01:00
|
|
|
// 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)
|
|
|
|
|
|
2025-11-22 16:42:35 +01:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-22 19:49:53 +01:00
|
|
|
const viewFederationGraph = () => {
|
|
|
|
|
if (!schema.value?.ref) return
|
|
|
|
|
navigateTo(`/graph/${schema.value.ref}`)
|
|
|
|
|
}
|
2025-11-22 16:42:35 +01:00
|
|
|
</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>
|