From bffe8cae14de5c1af7690ca3f1aabbe690e26996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 22 Jan 2021 21:33:20 +0100 Subject: [PATCH 1/3] fix: bugs in the Next generator --- README.md | 67 +++++++++---------- package.json | 2 +- src/generators/NextGenerator.js | 1 - src/index.js | 11 ++- .../next/components/common/ReferenceLinks.tsx | 3 +- templates/next/components/foo/Form.tsx | 57 +++++++++------- templates/next/components/foo/List.tsx | 5 +- templates/next/components/foo/Show.tsx | 27 ++++---- templates/next/pages/foos/[id]/edit.tsx | 2 +- templates/next/pages/foos/[id]/index.tsx | 4 +- 10 files changed, 92 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index dac69834..54dd1da1 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,15 @@ [![npm version](https://badge.fury.io/js/%40api-platform%2Fclient-generator.svg)](https://badge.fury.io/js/%40api-platform%2Fclient-generator) API Platform Client Generator is a generator to scaffold app with Create-Retrieve-Update-Delete features for any API exposing a [Hydra](http://www.hydra-cg.com/spec/latest/core/) or [OpenAPI](https://www.openapis.org/) documentation for: - * Quasar Framework - * Next.js - * React/Redux - * React Native - * TypeScript Interfaces - * Vue.js - * Vuetify.js + +* Next.js +* Nuxt.js +* Quasar Framework +* React/Redux +* React Native +* TypeScript Interfaces +* Vue.js +* Vuetify.js Works especially well with APIs built with the [API Platform](https://api-platform.com) framework. @@ -18,43 +20,34 @@ Works especially well with APIs built with the [API Platform](https://api-platfo The documentation of API Platform's Client Generator can be browsed [on the official website](https://api-platform.com/docs/client-generator). -## Usage - -**Hydra** -```sh -npx @api-platform/client-generator https://demo.api-platform.com/ output/ --resource Book -``` - -**OpenAPI v2 (formerly known as Swagger)** (experimental) -```sh -npx @api-platform/client-generator https://demo.api-platform.com/docs.json output/ --resource Book --format swagger -``` - -or - -```sh -npx @api-platform/client-generator https://demo.api-platform.com/docs.json output/ --resource Book --format openapi2 -``` - -**OpenAPI v3** (experimental) -```sh -npx @api-platform/client-generator https://demo.api-platform.com/docs.json?spec_version=3 output/ --resource Book --format openapi3 -``` - ## Features -* Generate high-quality ES6 components and files built with [React](https://facebook.github.io/react/), [Redux](http://redux.js.org), [React Router](https://reacttraining.com/react-router/) and [Redux Form](http://redux-form.com/) including: - * A list view - * A creation form - * An editing form - * A deletion button -* Use the Hydra or Swagger API documentation to generate the code +* Generate high-quality TypeScript or ES6 components: + * List view + * Creation form + * Editing form + * Deletion button +* Use the Hydra or OpenAPI documentations to generate the code * Generate the suitable HTML5 input type (`number`, `date`...) according to the type of the API property * Display of the server-side validation errors under the related input (if using API Platform Core) * Client-side validation (`required` attributes) * The generated HTML is compatible with [Bootstrap](https://getbootstrap.com/) and includes mandatory classes * The generated HTML code is accessible to people with disabilities ([ARIA](https://www.w3.org/WAI/intro/aria) support) -* The Redux and the React Router configuration is also generated + + +## Usage + +### Hydra + + npx @api-platform/client-generator https://demo.api-platform.com/ output/ --resource Book + +### OpenAPI v3 (experimental) + + npx @api-platform/client-generator https://demo.api-platform.com/docs.json?spec_version=3 output/ --resource Book --format openapi3 + +### OpenAPI v2 (formerly known as Swagger, deprecated) + + npx @api-platform/client-generator https://demo.api-platform.com/docs.json?spec_version=2 output/ --resource Book --format openapi2 ## Credits diff --git a/package.json b/package.json index f416bb7c..d12bc6ff 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@api-platform/client-generator", "version": "0.5.2", - "description": "Generate a CRUD application built with React, Redux and React Router from an Hydra-enabled API", + "description": "Generate apps built with Next, Nuxt, Quasar, React, React Native, Vue or Vuetify for any API documented using Hydra or OpenAPI", "files": [ "*.md", "docs/*.md", diff --git a/src/generators/NextGenerator.js b/src/generators/NextGenerator.js index a41d4f04..49f078d4 100644 --- a/src/generators/NextGenerator.js +++ b/src/generators/NextGenerator.js @@ -62,7 +62,6 @@ export default class NextGenerator extends BaseGenerator { `${dir}/config`, `${dir}/error`, `${dir}/types`, - `${dir}/pages/${context.lc}s/[id]`, `${dir}/utils`, ].forEach((dir) => this.createDir(dir, false)); diff --git a/src/index.js b/src/index.js index a9efc592..531bee3d 100755 --- a/src/index.js +++ b/src/index.js @@ -11,7 +11,7 @@ import generators from "./generators"; program .version(version) .description( - "Generate a CRUD application built with React, Redux and React Router from an Hydra-enabled API" + "Generate apps built with Next, Nuxt, Quasar, React, React Native, Vue or Vuetify for any API documented using Hydra or OpenAPI" ) .usage("entrypoint outputDirectory") .option( @@ -36,7 +36,11 @@ program "The templates directory base to use. Final directory will be ${templateDirectory}/${generator}", `${__dirname}/../templates/` ) - .option("-f, --format [hydra|swagger]", '"hydra" or "swagger', "hydra") + .option( + "-f, --format [hydra|openapi3|openapi2]", + '"hydra", "openapi3" or "openapi2"', + "hydra" + ) .option( "-s, --server-path [serverPath]", "Path to express server file to allow route dynamic addition (Next.js generator only)" @@ -83,7 +87,8 @@ const parser = (entrypointWithSlash) => { options.headers.set("Authorization", `Bearer ${program.bearer}`); } switch (program.format) { - case "swagger": + case "swagger": // deprecated + case "openapi2": return parseSwaggerDocumentation(entrypointWithSlash); case "openapi3": return parseOpenApi3Documentation(entrypointWithSlash); diff --git a/templates/next/components/common/ReferenceLinks.tsx b/templates/next/components/common/ReferenceLinks.tsx index 4ab8a761..60819fa1 100644 --- a/templates/next/components/common/ReferenceLinks.tsx +++ b/templates/next/components/common/ReferenceLinks.tsx @@ -6,7 +6,7 @@ interface Props { type: string; useIcon?: boolean; } -export const ReferenceLinks: FunctionComponent = ({ +const ReferenceLinks: FunctionComponent = ({ items, type, useIcon = false, @@ -38,3 +38,4 @@ export const ReferenceLinks: FunctionComponent = ({ ); }; +export default ReferenceLinks; diff --git a/templates/next/components/foo/Form.tsx b/templates/next/components/foo/Form.tsx index 13407e09..7a3710b3 100644 --- a/templates/next/components/foo/Form.tsx +++ b/templates/next/components/foo/Form.tsx @@ -1,8 +1,9 @@ import { FunctionComponent, useState } from "react"; -import { Formik } from "formik"; -import { {{{ucf}}} } from '../../types/{{{ucf}}}'; import Link from "next/link"; import { useRouter } from "next/router"; +import { Formik } from "formik"; +import { fetch } from "../../utils/dataAccess"; +import { {{{ucf}}} } from '../../types/{{{ucf}}}'; interface Props { {{{lc}}}?: {{{ucf}}}; @@ -12,33 +13,33 @@ export const Form: FunctionComponent = ({ {{{lc}}} }) => { const [error, setError] = useState(null); const router = useRouter(); - const handleDelete = () => { - if (window.confirm("Are you sure you want to delete this item?")) { - try { - fetch({{{lc}}}['@id'], { method: "DELETE" }); - router.push("/{{{name}}}"); - } catch (error) { - setError("Error when deleting the resource."); - console.error(error); - } - } + const handleDelete = async () => { + if (!window.confirm("Are you sure you want to delete this item?")) return; + + try { + await fetch({{{lc}}}['@id'], { method: "DELETE" }); + router.push("/{{{name}}}"); + } catch (error) { + setError("Error when deleting the resource."); + console.error(error); + } }; return (
- { {{{lc}}} ?

Edit {{{lc}}}['@id']

:

Create

} + { {{{lc}}} ?

Edit {{{ucf}}} { {{{lc}}}['@id'] }

:

Create {{{ucf}}}

} { const errors = {}; // add your validation logic here return errors; }} - onSubmit={(values, { setSubmitting, setStatus }) => { + onSubmit={async (values, { setSubmitting, setStatus }) => { const isCreation = !{{{lc}}}["@id"]; try { - fetch(isCreation ? "/{{{name}}}" : {{{lc}}}["@id"], { - method: isCreation ? "POST" : "PATCH", + await fetch(isCreation ? "/{{{name}}}" : {{{lc}}}["@id"], { + method: isCreation ? "POST" : "PUT", body: JSON.stringify(values), }); setStatus({ @@ -57,27 +58,31 @@ export const Form: FunctionComponent = ({ {{{lc}}} }) => { > {({ values, - status, + status, + errors, + touched, handleChange, handleBlur, handleSubmit, isSubmitting, }) => (
-{{#each fields}} +{{#each formFields}}
- +
- { errors.{{name}} && touched.{{name}} && errors.{{name}} } + { errors.{{name}} && touched.{{name}} &&
{ errors.{{name}} }
} {{/each}} {status && status.msg && (
= ({ {{{lc}}} }) => { const [error, setError] = useState(null); const router = useRouter(); - const handleDelete = () => { - if (window.confirm("Are you sure you want to delete this item?")) { - try { - fetch({{{lc}}}["@id"], { method: "DELETE" }); - router.push("/{{{name}}}"); - } catch (error) { - setError("Error when deleting the resource."); - console.error(error); - } - } + const handleDelete = async () => { + if (!window.confirm("Are you sure you want to delete this item?")) return; + + try { + await fetch({{{lc}}}["@id"], { method: "DELETE" }); + router.push("/{{{name}}}"); + } catch (error) { + setError("Error when deleting the resource."); + console.error(error); + } }; return ( @@ -50,8 +51,8 @@ export const Show: FunctionComponent = ({ {{{lc}}} }) => { )} Back to list - - + {" "} + Edit