Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2bb19b1
feat(base.scss): add base styling
vaibhavrajsingh2001 Apr 24, 2024
d71dd3f
feat(model-properties): add component to render out object properties
vaibhavrajsingh2001 Apr 24, 2024
f1119ad
feat(model-node): render model info and properties
vaibhavrajsingh2001 Apr 24, 2024
730d17b
chore(base.scss): move to sandbox assets
vaibhavrajsingh2001 Apr 25, 2024
c66f761
feat(model): add example, enum and format fields
vaibhavrajsingh2001 Apr 25, 2024
afcce30
feat(model): add SchemaObject type def from openapi3-ts
vaibhavrajsingh2001 Apr 25, 2024
a7854b6
feat(model-properties): add support for arrays
vaibhavrajsingh2001 Apr 26, 2024
ecd0ff5
feat(model-properties): add support for enums
vaibhavrajsingh2001 Apr 26, 2024
abd9a04
feat(openapi3-ts): add wrapper types for openapi3-ts library
vaibhavrajsingh2001 Apr 26, 2024
5312fe4
feat(model-node): add support for top level node of array type
vaibhavrajsingh2001 Apr 26, 2024
293d89c
feat(model-properties): support for array item of type object
vaibhavrajsingh2001 Apr 26, 2024
f25f705
feat(document-component): pass title prop
vaibhavrajsingh2001 Apr 29, 2024
0004c4b
chore: rename required fields prop
vaibhavrajsingh2001 Apr 29, 2024
64f5c6f
fix(model-properties): use details element for array item fields
vaibhavrajsingh2001 Apr 29, 2024
b2001f3
fix(mode-properties): add horizntal padding to show nesting
vaibhavrajsingh2001 Apr 29, 2024
04c5928
feat(mode-node): add example field
vaibhavrajsingh2001 Apr 29, 2024
be4fc77
chore: use CSS custom properties when using design tokens
vaibhavrajsingh2001 Apr 29, 2024
b03b421
chore(model-properties): remove extra spaces/lines
vaibhavrajsingh2001 Apr 29, 2024
5c4b0cd
chore(spec-renderer): add comments for wrapper types
vaibhavrajsingh2001 Apr 29, 2024
2f22e57
fix(model-node): wrap props in toRefs before destructuring to preserv…
vaibhavrajsingh2001 Apr 29, 2024
3ec39da
chore(model-properties): move property checks to v-if
vaibhavrajsingh2001 Apr 29, 2024
1756f5a
fix(model-node): remove deep destructuring of props
vaibhavrajsingh2001 May 1, 2024
8adf1f1
fix(model-node): add docs for properties compute
vaibhavrajsingh2001 May 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/components/Document/DocumentComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
:is="docComponent(serviceNode)"
v-if="serviceNode"
:data="serviceNode.data"
:title="serviceNode.name"
/>
</template>

Expand Down
58 changes: 50 additions & 8 deletions src/components/Document/ModelNode.vue
Original file line number Diff line number Diff line change
@@ -1,18 +1,60 @@
<template>
Model
<br>
{{ data }}
<h3>
{{ title }} <code v-if="data.type">{{ data.type }}</code>
</h3>
<p v-if="data.description">
{{ data.description }}
</p>
<p v-if="data.example || data.examples">
Example: {{ data.example || data.examples }}
</p>
<p v-if="data.enum">
<span>Allowed values: </span> {{ data.enum }}
</p>

<ModelProperties
v-if="modelPropertiesProps"
:properties="modelPropertiesProps.properties"
:required-fields="modelPropertiesProps.required"
/>
</template>

<script setup lang="ts">
import type { PropType } from 'vue'
import type { INode } from '@stoplight/types'
import { computed, toRefs } from 'vue'
import ModelProperties from './ModelProperties.vue'

import { isValidSchemaObject } from '@/utils'

import type{ PropType } from 'vue'
import type { SchemaObject } from '@/types'

// TODO: type def is missing for model need to add
defineProps({
const props = defineProps({
data: {
type: Object as PropType<INode>,
type: Object as PropType<SchemaObject>,
required: true,
},
title: {
type: String,
required: true,
},
})

const modelPropertiesProps = computed(() => {
const { data } = toRefs(props)
let computedObj: Partial<SchemaObject> | null = null

/**
* We have to enumerate over the properties of the Schema Model and render them out via `ModelProperties` component.
* For this, we need to compute the properties and required fields of the Schema Model.
* If the top level Schema Model is an object, we can directly use the `properties` field of the object.
* If it's an array, we need to derive the properties from the `items` field of the Schema Model.
*/
if (data.value.type === 'object' && data.value.properties && Reflect.ownKeys(data.value.properties).length) {
computedObj = { properties: data.value.properties, required: data.value.required }
} else if (data.value.type === 'array' && isValidSchemaObject(data.value.items)) {
computedObj = { properties: data.value.items.properties, required: data.value.items.required }
}

return computedObj
})
</script>
108 changes: 108 additions & 0 deletions src/components/Document/ModelProperties.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<template>
<div
v-for="(property, key) in properties"
:key="key"
class="model-property"
>
<template v-if="isValidSchemaObject(property)">
<div class="property-info">
<code>{{ key }}</code>
<span class="property-type">
{{ property.type ?? '' }}
{{ isValidSchemaObject(property.items) && property.items.type ? `[${property.items.type}]` : '' }}
{{ property.format ? `(${property.format})` : '' }}
</span>
<span
v-if="requiredFields?.includes(key.toString())"
class="required-property"
>
required
</span>
</div>
<p v-if="property.description">
{{ property.description }}
</p>
<p v-if="property.example">
<span>Example: </span> {{ property.example }}
</p>
<p v-if="property.enum">
Allowed values: {{ property.enum }}
</p>

<template v-if="isValidSchemaObject(property.items)">
<p v-if="property.items.type === 'string' && (property.items.enum || property.items.pattern)">
<span v-if="property.items.enum">Allowed values: {{ property.items.enum }}</span>
<span v-else-if="property.items.pattern">Allowed pattern: <code>{{ property.items.pattern }}</code></span>
</p>
<p v-else-if="property.items.type === 'integer' && (property.items.maximum || property.items.minimum)">
<span v-if="property.items.maximum">Max: {{ property.items.maximum }}</span>
<span v-if="property.items.maximum && property.items.minimum">|</span>
<span v-if="property.items.minimum"> Min: {{ property.items.minimum }}</span>
</p>
<details v-else-if="isNestedObj(property.items)">
<summary>Properties of items in <code>{{ key }}</code></summary>
<ModelProperties
:properties="property.items.properties"
:required-fields="property.items.required"
/>
</details>
</template>

<details v-if="isNestedObj(property)">
<summary>Properties of <code>{{ key }}</code></summary>
<ModelProperties
:properties="property.properties"
:required-fields="property.required"
/>
</details>
</template>

<div v-else>
<code>{{ key }}</code>
</div>
</div>
</template>

<script setup lang="ts">
import type { PropType } from 'vue'
import { isValidSchemaObject } from '@/utils'
import type { SchemaObject } from '@/types'

defineProps({
properties: {
type: Object as PropType<SchemaObject['properties']>,
required: true,
},
requiredFields: {
type: Array as PropType<SchemaObject['required']>,
default: () => [],
},
})

const isNestedObj = (property: SchemaObject) => property.type === 'object' && property.properties && Reflect.ownKeys(property.properties).length
</script>

<style lang="scss" scoped>
.model-property {
border-bottom: var(--kui-border-width-10, $kui-border-width-10) solid var(--kui-color-border, $kui-color-border);
padding: var(--kui-space-50, $kui-space-50) var(--kui-space-80, $kui-space-80);

&>:not(:first-child) {
margin-top: var(--kui-space-40, $kui-space-40);
}
}

.property-info>:not(:first-child) {
margin-left: var(--kui-space-40, $kui-space-40);
}

.property-type {
color: var(--kui-color-text-neutral-strong, $kui-color-text-neutral-strong);
font-size: var(--kui-font-size-20, $kui-font-size-20);
}

.required-property {
color: var(--kui-color-text-danger, $kui-color-text-danger);
font-size: var(--kui-font-size-20, $kui-font-size-20);
}
</style>
2 changes: 1 addition & 1 deletion src/components/SpecRenderer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ aside {
background-color: lightblue;
flex: 1;
overflow: visible;
padding-left: $kui-space-60;
padding-left: var(--kui-space-60, $kui-space-60);
}
.wrapper {
display: flex;
Expand Down
10 changes: 10 additions & 0 deletions src/types/spec-renderer.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
import type { SchemaObject as OAS31SchemaObject, ReferenceObject as OAS31ReferenceObject } from 'openapi3-ts/oas31'

export interface SpecRendererProps {
modelValue: string
}

/**
* Wrapper types so that we don't import/use types from a 3rd party library, like openapi3-ts, directly in our code.
* This way it'll be easy to replace out this library with some other library, or even our own implementation,
* without requiring major refactoring.
*/
export interface SchemaObject extends OAS31SchemaObject {}
export interface ReferenceObject extends OAS31ReferenceObject {}
9 changes: 9 additions & 0 deletions src/utils/schema-parser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import composables from '../composables'
import type { SchemaObject, ReferenceObject } from '@/types'

const {
parse,
Expand All @@ -8,10 +9,18 @@ const {
validationResults,
} = composables.useSchemaParser()

/**
* Type guard for verifying object is of type SchemaObject
*/
function isValidSchemaObject(candidate?: SchemaObject | ReferenceObject): candidate is SchemaObject {
return Boolean(candidate && !Object.prototype.hasOwnProperty.call(candidate, '$ref'))
}

export {
parse,
parsedDocument,
jsonDocument,
tableOfContents,
validationResults,
isValidSchemaObject,
}