Skip to content

Commit

Permalink
Merge pull request appwrite#793 from appwrite/fix-nested-model
Browse files Browse the repository at this point in the history
Add support for nested models
  • Loading branch information
stnguyen90 authored Mar 22, 2024
2 parents 297c2fc + 5f51a91 commit fde477a
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 27 deletions.
80 changes: 78 additions & 2 deletions src/lib/utils/specs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ export type AppwriteSchemaObject = OpenAPIV3.SchemaObject & {
'x-example': string;
};

export interface Property {
name: string;
items?: {
type?: string;
oneOf?: OpenAPIV3.ReferenceObject[];
} & OpenAPIV3.ReferenceObject;
}

function getExamples(version: string) {
switch (version) {
case '0.15.x':
Expand Down Expand Up @@ -169,8 +177,7 @@ export function getSchema(id: string, api: OpenAPIV3.Document): OpenAPIV3.Schema
if (schema) {
return schema;
}
throw new Error("Schema doesn't exist");
}
throw new Error(`Schema doesn't exist for id: ${id}`);}

const specs = import.meta.glob(
'$appwrite/app/config/specs/open-api3*-(client|server|console).json',
Expand Down Expand Up @@ -336,3 +343,72 @@ export function resolveReference(
}
throw new Error("Schema doesn't exist");
}

export const generateExample = (schema: OpenAPIV3.SchemaObject, api: OpenAPIV3.Document<{}>): Object => {

const properties = Object.keys(schema.properties ?? {}).map((key) =>{
const name = key;
const fields = schema.properties?.[key];
return {
name,
...fields
}
});

const example = properties.reduce((carry, currentValue) => {
const property = currentValue as AppwriteSchemaObject & Property;
if (property.type === 'array') {
// If it's an array type containing primatives
if (property.items?.type){
return {
...carry,
[property.name]: property['x-example']
}
}

if (property.items && 'anyOf' in property.items) {
// default to first child type if multiple available
const firstSchema = (property.items as unknown as AppwriteSchemaObject)?.anyOf?.[0];
const schema = getSchema(getIdFromReference(firstSchema as OpenAPIV3.ReferenceObject), api)

return {
...carry,
[property.name]: [generateExample(schema, api)]
};
}

// if an array of objects without child types
const schema = getSchema(getIdFromReference(property.items as OpenAPIV3.ReferenceObject), api);
return {
...carry,
[property.name]: [generateExample(schema, api)]
}
}

// If it's an object type, but not in an array.
if (property.type === 'object') {
if (property.items?.oneOf){
// default to first child type if multiple available
const schema = getSchema(getIdFromReference(property.items.oneOf[0] as OpenAPIV3.ReferenceObject), api);
return {
...carry,
[property.name]: generateExample(schema, api)
}
}

if (property.items){
const schema = getSchema(getIdFromReference(property.items), api);
return {
...carry,
[property.name]: generateExample(schema, api)
}
}
}

return {
...carry,
[property.name]: property['x-example']
}
}, {});
return example;
}
80 changes: 56 additions & 24 deletions src/routes/docs/references/[version]/models/[model]/+page.server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getApi, getIdFromReference, getSchema, type AppwriteSchemaObject } from '$lib/utils/specs';
import { getApi, getSchema, type AppwriteSchemaObject, generateExample, type Property } from '$lib/utils/specs';
import type { OpenAPIV3 } from 'openapi-types';
import type { PageServerLoad } from './$types';

Expand All @@ -8,8 +8,8 @@ type Model = {
name: string;
type: string;
description: string;
example: string | boolean | number | object | Array<unknown>;
items?: Array<string>;
items?: Array<any>;
relatedModels?: string;
}>;
};

Expand All @@ -21,51 +21,83 @@ export const load: PageServerLoad = async ({ params }) => {
const model: Model = {
title: schema.description as string,
properties: props.map(([name, data]) => {
const property = data as AppwriteSchemaObject;
const property = data as AppwriteSchemaObject & Property;
switch (property.type) {
case 'array': {
const items = [];
const propItems = property.items as AppwriteSchemaObject;
if (Array.isArray(propItems.anyOf)) {
items.push(
...propItems.anyOf.map((ref) =>
getIdFromReference(ref as OpenAPIV3.ReferenceObject)
let arrayTypes;
if (property.items.hasOwnProperty('$ref')) {
arrayTypes = [
(property.items.$ref as string)
.split('/')
.pop()
];
}

if (property.items && 'anyOf' in property.items) {
arrayTypes = (property.items as OpenAPIV3.SchemaObject).anyOf?.map(
item => (
(item as OpenAPIV3.ReferenceObject).$ref as string
)
.split('/')
.pop()
);
} else {
// items.push(getIdFromReference(propItems as unknown as OpenAPIV3.ReferenceObject));
}

return {
name,
type: property.type as string,
description: property.description as string,
example: '',
items:
(property.items as AppwriteSchemaObject)?.anyOf?.map((ref) => {
const item = getIdFromReference(ref as OpenAPIV3.ReferenceObject);
return item;
}) ?? []
relatedModels: arrayTypes?.map((item) => {
const schema = getSchema(item as string, api);
const modelLink = `[${schema.description} model](/docs/references/${version}/models/${item})`;
return modelLink;
}).join(', ') ?? ''
};
}
case 'object': {
let arrayTypes;
if (property.items?.hasOwnProperty('$ref')) {
arrayTypes = [
((property.items)?.$ref as string)
.split('/')
.pop()
];
}

if (property.items && 'oneOf' in property.items) {
arrayTypes = property.items.oneOf?.map(
item => (
item.$ref as string
)
.split('/')
.pop()
);
}
return {
name,
type: property.type as string,
description: property.description as string,
relatedModels: arrayTypes?.map((item) => {
const schema = getSchema(item as string, api);
const modelLink = `[${schema.description} model](/docs/references/${version}/models/${item})`;
return modelLink;
}).join(', ') ?? '',
};
}
default:
return {
name,
type: property.type as string,
description: property.description as string,
example: property['x-example']
};
}
})
};

const example = model.properties.reduce<Record<string, unknown>>((carry, property) => {
carry[property.name] = property.example;
const example = generateExample(schema, api);

return carry;
}, {});
return {
model,
example
};
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { page } from '$app/stores';
import Article from '$markdoc/layouts/Article.svelte';
import { Table, Thead, Tr, Th, Tbody, Td, Heading, Fence } from '$markdoc/nodes/_Module.svelte';
import { parse } from '$lib/utils/markdown';
export let data;
</script>
Expand Down Expand Up @@ -34,7 +35,12 @@
<Tr>
<Td>{property.name}</Td>
<Td>{property.type}</Td>
<Td>{property.description}</Td>
<Td>
{property.description}
{#if property.relatedModels}
Can be one of: {@html parse(property.relatedModels)}
{/if}
</Td>
</Tr>
{/each}
</Tbody>
Expand Down

0 comments on commit fde477a

Please sign in to comment.