Skip to content

Commit

Permalink
refactor(open-api): resolve type safety issues and create util file f…
Browse files Browse the repository at this point in the history
…or OpenAPI type guards

INT-407
  • Loading branch information
FreekVR committed Apr 10, 2024
1 parent ce48ab8 commit 4539476
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 35 deletions.
36 changes: 23 additions & 13 deletions src/.vuepress/theme/client/components/global/OpenApi.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,32 @@ const props = defineProps<{
const resolvedDocument = computed(() => resolveRefs(props.document));
// Recursively loop through the entire document and replace any $ref keys with their corresponding values
type RecursiveType = {
[key: string]: unknown | RecursiveType | RecursiveType[];
};
function resolveRefs(document: OpenApiType.Document): OpenApiType.Document {
const resolvedDocument = {...document};
function resolveRefsRecursive(obj: object | (string | number | object)[]) {
for (const key in obj) {
if (key === '$ref') {
const lookup = (obj as {$ref: string})[key];
// Lookup is a string that defines the path to the referenced object like '#/components/schemas/Example'
const path = lookup.split('/').slice(1);
// Find the referenced object in the document using lodash's get function
const referencedObject = get(resolvedDocument, path);
// Remove the $ref: '...' key from the object and replace it with the referenced object itself
obj = Object.assign(obj, referencedObject);
} else if (typeof obj[key] === 'object' || Array.isArray(obj[key])) {
// If the value is an object or an array, recursively call this function
resolveRefsRecursive(obj[key]);
function resolveRefsRecursive(obj: RecursiveType) {
if (Array.isArray(obj)) {
for (const item of obj) {
resolveRefsRecursive(item);
}
} else if (typeof obj === 'object') {
for (const key in obj) {
if (key === '$ref') {
// Lookup is a string that defines the path to the referenced object like '#/components/schemas/Example'
const path = key.split('/').slice(1);
// Find the referenced object in the document using lodash's get function
const referencedObject = get(resolvedDocument, path);
// Remove the $ref: '...' key from the object and replace it with the referenced object itself
obj = Object.assign(obj, referencedObject);
} else if (typeof obj[key] === 'object' || Array.isArray(obj[key])) {
const guarded = obj[key] as RecursiveType;
// If the value is an object or an array, recursively call this function
resolveRefsRecursive(guarded);
}
}
}
}
Expand Down
11 changes: 4 additions & 7 deletions src/.vuepress/theme/client/components/global/OpenApiPath.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@
v-for="(content, key) in operation.requestBody.content"
:key="key">
<strong>{{ key }}</strong>
<OpenApiSchema :schema="content?.schema" />
<OpenApiSchema
v-if="content.schema && (isSchemaObject(content.schema) || isArraySchemaObject(content.schema))"
:schema="content.schema" />
</div>
</DetailsExpand>
</template>
Expand All @@ -63,6 +65,7 @@

<script setup lang="ts">
import {type OpenAPIV3_1 as OpenApiType} from 'openapi-types';
import {isParameterType, isArraySchemaObject, isSchemaObject} from '@mptheme/client/utils/openApiGuards';
import OpenApiSchema from './OpenApiSchema.vue';
import OpenApiResponses from './OpenApiResponses.vue';
import OpenApiRequestParam from './OpenApiRequestParam.vue';
Expand All @@ -74,10 +77,4 @@ defineProps<{
title: string;
path: OpenApiType.PathItemObject;
}>();
function isParameterType(
parameter: OpenApiType.ParameterObject | OpenApiType.ReferenceObject,
): parameter is OpenApiType.ParameterObject {
return 'in' in parameter && 'schema' in parameter;
}
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
<script setup lang="ts">
import {computed, type ComputedRef} from 'vue';
import {type OpenAPIV3_1 as OpenApiType} from 'openapi-types';
import {isReponseObject} from '@mptheme/client/utils/openApiGuards';
import OpenApiSchema from './OpenApiSchema.vue';
import Http from './Http.vue';
import DetailsExpand from './DetailsExpand.vue';
Expand All @@ -74,11 +75,6 @@ const props = defineProps<{
responses: OpenApiType.ResponsesObject;
}>();
// Guard to check if the response is an OpenApiType.ResponseObject.
const isReponseObject = (response: object): response is OpenApiType.ResponseObject => {
return typeof response === 'object';
};
// Adds a computed to get only the OpenApiType.ResponseObject types from the responses prop.
const responseObjects: ComputedRef<Record<string, OpenApiType.ResponseObject>> = computed(() => {
const objects: Record<string, OpenApiType.ResponseObject> = {};
Expand Down
11 changes: 1 addition & 10 deletions src/.vuepress/theme/client/components/global/OpenApiSchema.vue
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
<script setup lang="ts">
import {ref} from 'vue';
import {type OpenAPIV3_1 as OpenApiType} from 'openapi-types';
import {isArraySchemaObject, isSchemaObject} from '@mptheme/client/utils/openApiGuards';
import OpenApiSchemaInfo from './OpenApiSchemaInfo.vue';
defineProps<{
Expand All @@ -141,16 +142,6 @@ defineProps<{
const showNested = ref(false);
// Guard to check if the schema is an OpenApiType.SchemaObject.
const isSchemaObject = (schema: object): schema is OpenApiType.SchemaObject => {
return typeof schema === 'object';
};
// Guard to check if the schema is an OpenApiType.ArraySchemaObject.
const isArraySchemaObject = (schema: object): schema is OpenApiType.ArraySchemaObject => {
return typeof schema === 'object' && 'items' in schema;
};
function anyPropertyHasNesting(properties: OpenApiType.SchemaObject['properties']) {
return properties
? Object.keys(properties).some((key) => propertyHasNesting(key, Object.keys(properties[key])))
Expand Down
22 changes: 22 additions & 0 deletions src/.vuepress/theme/client/utils/openApiGuards.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {type OpenAPIV3_1 as OpenApiType} from 'openapi-types';

export function isParameterType(
parameter: OpenApiType.ParameterObject | OpenApiType.ReferenceObject,
): parameter is OpenApiType.ParameterObject {
return 'in' in parameter && 'schema' in parameter;
}

// Guard to check if the schema is an OpenApiType.SchemaObject.
export const isSchemaObject = (schema: object): schema is OpenApiType.SchemaObject => {
return typeof schema === 'object';
};

// Guard to check if the schema is an OpenApiType.ArraySchemaObject.
export const isArraySchemaObject = (schema: object): schema is OpenApiType.ArraySchemaObject => {
return typeof schema === 'object' && 'items' in schema;
};

// Guard to check if the response is an OpenApiType.ResponseObject.
export const isReponseObject = (response: object): response is OpenApiType.ResponseObject => {
return typeof response === 'object';
};

0 comments on commit 4539476

Please sign in to comment.