[JAVA] Comment créer votre propre API headless à l'aide de REST Builder de Liferay (partie 3)

introduction

[Partie 1] de cette série (https://liferay.dev/blogs/-/blogs/creating-headless-apis-part-1) exploite le nouvel outil REST Builder de Liferay pour générer des API headless. A commencé un projet à faire. Dans la section Composants réutilisables, nous avons défini les définitions des objets de requête et de réponse: le composant vitaminique et une copie du composant Creator de Liferay.

Dans la partie 2 de la série, nous avons complété le fichier OpenAPI Yaml en définissant des chemins (points de terminaison) et avons généré avec succès le code malgré les problèmes courants.

Dans cette partie, nous verrons comment ajouter du code d'implémentation à l'endroit approprié dans le code généré.

Lire le code généré

Il existe quatre modules générés par code: «headless-vitamin-api», «headless-vitamin-client», «headless-vitamin-impl» et «headless-vitamin-test».

Regardons chaque module individuellement.

headless-vitamins-api

Le concept de module API est similaire au module API Service Builder, qui comprend une interface pour les ressources (services). Il contient également des classes POJO spécifiques pour les types de composants (Vitamine et Créateur). Ce ne sont pas seulement des POJO, la classe de type de composant a des setters supplémentaires qui sont appelés par le framework lors de la désérialisation des objets. Jetons un coup d'œil à l'un des types de composants Creator.

@JsonIgnore
public void setAdditionalName(
	UnsafeSupplier additionalNameUnsafeSupplier) {

	try {
		additionalName = additionalNameUnsafeSupplier.get();
	}
	catch (RuntimeException re) {
		throw re;
	}
	catch (Exception e) {
		throw new RuntimeException(e);
	}
}

Le code généré ci-dessus est très simple, mais ne vous inquiétez pas.

VitaminResource est l'interface de la ressource (service) et provient du chemin défini dans le fichier OpenAPI Yaml. Après avoir appelé REST Builder, vous remarquerez peut-être que de nouveaux attributs sont ajoutés à chaque chemin de ʻoperationId` dans le fichier yaml et que ces valeurs correspondent exactement aux méthodes de l'interface.

Le code généré seul a trop peu de méthodes, nous allons donc partager l'interface ici.

@Generated("")
public interface VitaminResource {

	public Page getVitaminsPage(
			String search, Filter filter, Pagination pagination, Sort[] sorts)
		throws Exception;

	public Vitamin postVitamin(Vitamin vitamin) throws Exception;

	public void deleteVitamin(String vitaminId) throws Exception;

	public Vitamin getVitamin(String vitaminId) throws Exception;

	public Vitamin patchVitamin(String vitaminId, Vitamin vitamin)
		throws Exception;

	public Vitamin putVitamin(String vitaminId, Vitamin vitamin)
		throws Exception;

	public void setContextCompany(Company contextCompany);

}

Le chemin / vitamins qui renvoie la séquence des objets vitamines est la première méthode getVitaminsPage (). Votre propre fichier Yaml ne déclare pas de composant PageVitamin, mais un est inséré dans le fichier Yaml exporté.

Les autres méthodes de l'interface de ressources correspondent aux autres chemins définis dans le fichier Yaml. Ensuite, j'ai dû ajouter des dépendances au fichier build.gradle du module API:

dependencies {
	compileOnly group: "com.fasterxml.jackson.core", name: "jackson-annotations", version: "2.9.9"
	compileOnly group: "com.liferay", name: "com.liferay.petra.function"
	compileOnly group: "com.liferay", name: "com.liferay.petra.string"
	compileOnly group: "com.liferay", name: "com.liferay.portal.vulcan.api"
	compileOnly group: "com.liferay.portal", name: "com.liferay.portal.kernel"
	compileOnly group: "io.swagger.core.v3", name: "swagger-annotations", version: "2.0.5"
	compileOnly group: "javax.servlet", name: "javax.servlet-api"
	compileOnly group: "javax.validation", name: "validation-api", version: "2.0.1.Final"
	compileOnly group: "javax.ws.rs", name: "javax.ws.rs-api"
	compileOnly group: "org.osgi", name: "org.osgi.annotation.versioning"
}

Nous avons également apporté des modifications mineures au fichier bnd.bnd pour exposer les composants et les interfaces de ressources:

Export-Package: com.dnebinger.headless.vitamins.dto.v1_0, \
    com.dnebinger.headless.vitamins.resource.v1_0

headless-vitamins-client

Le code de ce module crée un client Java pour appeler des API headless.

Le point d'entrée du client se trouve dans la classe <package prefix> .client.resource.v1_0 <Component> Resource. Dans mon cas, il s'agit de la classe com.dnebinger.headless.vitamins.client.resource.v1_0.VitaminResource.

Chaque chemin a une méthode statique, chaque méthode prend les mêmes arguments et renvoie le même objet. Dans les coulisses, chaque méthode utilise une instance HttpInvoker pour appeler le service Web à localhost: 8080 avec [email protected] et tester les informations de connexion. Si vous testez un service distant ou souhaitez utiliser des informations de connexion différentes, vous devrez modifier la classe <Component> Resource en conséquence pour utiliser des valeurs différentes.

C'est au concepteur d'écrire la classe principale et les autres codes pour appeler le code client, mais avoir une bibliothèque cliente complète pour les tests est une excellente première étape!

Le module headless-vitamins-client n'a pas de dépendances externes, mais vous devez exporter le package du fichier bnd.bnd.

Export-Package: com.dnebinger.headless.vitamins.client.dto.v1_0, \
    com.dnebinger.headless.vitamins.client.resource.v1_0

headless-vitamins-test

Passons le module «headless-vitamins-impl» et décrivons brièvement «headless-vitamins-test».

Le code généré ici fournit tous les tests d'intégration pour le module de service et exploite le module client pour appeler des API distantes. Dans ce module, nous avons deux classes, Base <Component> ResourceTestCase et <Component> ResourceTestCase, donc nous avons BaseVitaminResourceTestCase et VitaminResourceTest.

La classe VitaminResourceTest est l'endroit où vous ajoutez des tests que la classe Base n'a pas encore implémentés. Il s'agit d'un test à grande échelle pour utiliser d'autres modules, et est utilisé pour la vérification des erreurs lorsque vous essayez d'ajouter une clé primaire en double ou de supprimer un objet inexistant. Fondamentalement, il s'agit d'un test qui ne peut pas être couvert individuellement par un simple appel à une méthode de ressource simple.

Le fichier build.gradle pour ce module a nécessité de nombreux ajouts:


dependencies {
	testIntegrationCompile group: "com.fasterxml.jackson.core", name: "jackson-annotations", version: "2.9.9"
	testIntegrationCompile group: "com.fasterxml.jackson.core", name: "jackson-core", version: "2.9.9"
	testIntegrationCompile group: "com.fasterxml.jackson.core", name: "jackson-databind", version: "2.9.9.1"
	testIntegrationCompile group: "com.liferay", name: "com.liferay.arquillian.extension.junit.bridge", version: "1.0.19"
	testIntegrationCompile group: "com.liferay.portal", name: "com.liferay.portal.kernel"
	testIntegrationCompile project(":modules:headless-vitamins:headless-vitamins-api")
	testIntegrationCompile project(":modules:headless-vitamins:headless-vitamins-client")
	testIntegrationCompile group: "com.liferay", name: "com.liferay.portal.odata.api"
	testIntegrationCompile group: "com.liferay", name: "com.liferay.portal.vulcan.api"
	testIntegrationCompile group: "com.liferay", name: "com.liferay.petra.function"
	testIntegrationCompile group: "com.liferay", name: "com.liferay.petra.string"
	testIntegrationCompile group: "javax.validation", name: "validation-api", version: "2.0.1.Final"
	testIntegrationCompile group: "commons-beanutils", name: "commons-beanutils"
	testIntegrationCompile group: "commons-lang", name: "commons-lang"
	testIntegrationCompile group: "javax.ws.rs", name: "javax.ws.rs-api"
	testIntegrationCompile group: "junit", name: "junit"
	testIntegrationCompile group: "com.liferay.portal", name: "com.liferay.portal.test"
	testIntegrationCompile group: "com.liferay.portal", name: "com.liferay.portal.test.integration"
}

Certaines de ces dépendances sont les valeurs par défaut nécessaires uniquement pour les classes (modules de test junit et liferay), et d'autres dépendent du projet (modules client et api, et éventuellement d'autres modules). Cela peut prendre quelques essais et erreurs pour obtenir une liste qui répond à vos besoins.

Le fichier bnd.bnd de ce module n'exporte pas de classes ou de packages et n'a pas besoin d'être modifié.

headless-vitamins-impl

Ça devient enfin intéressant. C'est le module qui contient le code d'implémentation. REST Builder a généré beaucoup de code de démarrage. Voyons à quoi ça ressemble.

com.dnebinger.headless.vitamins.internal.graphql, GraphQL est là! L'implémentation headless inclut des points de terminaison GraphQL qui exposent des requêtes et des mutations basées sur des chemins définis. Notez que GraphQL gère les requêtes et les changements de mutation en appelant directement <Component> Resource, plutôt que de simplement renvoyer les appels à l'implémentation REST couramment trouvée dans ce type de mix. .. Par conséquent, vous pouvez obtenir GraphQL automatiquement simplement en utilisant REST Builder.

com.dnebinger.headless.vitamins.internal.jaxrs.application, c'est là que la classe Application JAX-RS est stockée. Il ne contient rien de particulièrement intéressant, mais il enregistre l'application dans le conteneur OSGi de Liferay.

com.dnebinger.headless.vitamins.internal.resource.v1_0, c'est ici que vous corrigez votre code.

«La classe OpenAPIResourceImpl.java» est le chemin pour renvoyer le fichier yaml OpenAPI à charger dans Swagger Hub, par exemple. Pour chaque interface <Component> Resource, récupérez la classe de base abstraite Base <Component> ResourceImpl et la classe concrète <Component> ResourceImpl pour effectuer le travail. Par conséquent, il existe deux classes, «BaseVitaminResourceImpl» et «VitaminResourceImpl».

Si vous regardez les méthodes de la classe de base, vous pouvez voir qu'elles sont fortement décorées d'annotations Swagger et JAX-RS. Jetons un coup d'œil à l'une des méthodes getVitaminsPage () utilisées pour renvoyer un tableau de composants Vitamin stockés dans / vitamins:

@Override
@GET
@Operation(
  description = "Retrieves the list of vitamins and minerals. Results can be paginated, filtered, searched, and sorted."
)
@Parameters(
  value = {
    @Parameter(in = ParameterIn.QUERY, name = "search"),
    @Parameter(in = ParameterIn.QUERY, name = "filter"),
    @Parameter(in = ParameterIn.QUERY, name = "page"),
    @Parameter(in = ParameterIn.QUERY, name = "pageSize"),
    @Parameter(in = ParameterIn.QUERY, name = "sort")
  }
)
@Path("/vitamins")
@Produces({"application/json", "application/xml"})
@Tags(value = {@Tag(name = "Vitamin")})
public Page<Vitamin> getVitaminsPage(
    @Parameter(hidden = true) @QueryParam("search") String search,
    @Context Filter filter, @Context Pagination pagination,
    @Context Sort[] sorts)
  throws Exception {

  return Page.of(Collections.emptyList());
}

Comment c'est?

C'est l'un des avantages que REST Builder nous apporte. Toutes les annotations sont définies dans la classe de base, vous n'avez donc pas à vous en soucier.

Jetons un coup d'œil à l'instruction return passant Page.of (Collections.emptyList ()). Il s'agit de la méthode stub fournie par la classe de base. Il ne fournit pas une implémentation valable, mais il garantit qu'une valeur est renvoyée si elle n'est pas implémentée.

Lorsque vous êtes prêt à implémenter cette méthode, ajoutez la méthode suivante à la classe VitaminResourceImpl (actuellement vide):

@Override
public Page<Vitamin> getVitaminsPage(String search, Filter filter, Pagination pagination, Sort[] sorts) throws Exception {
  List<Vitamin> vitamins = new ArrayList<Vitamin>();
  long totalVitaminsCount = ...;

  // write code here, should add to the list of Vitamin objects

  return Page.of(vitamins, Pagination.of(0, pagination.getPageSize()), totalVitaminsCount);
}

Notez qu'il n'y a pas d'annotations.

Comme mentionné précédemment, toutes les annotations sont incluses dans la méthode de remplacement, donc toutes les configurations sont prêtes! Par conséquent, contrairement au code généré par Service Builder, le commentaire "Ce fichier a été généré, mais veuillez ne pas modifier ce fichier" n'apparaît nulle part. Si vous exécutez à nouveau REST Builder, vous verrez l'annotation `@Generated (" ") ʻ pour toutes les classes (re) générées.

La classe Base <Component> ResourceImpl est annotée comme ceci: Il s'agit d'un fichier qui est régénéré chaque fois que vous exécutez REST Builder. Par conséquent, ne modifiez pas les annotations ou les implémentations de méthode dans ce fichier. Apportez toutes les modifications à la classe <Component> ResourceImpl.

Si vous avez besoin de changer les annotations (** non recommandé **), vous pouvez le faire avec la classe <Component> ResourceImpl et vous devez remplacer les annotations de la classe de base. Par conséquent, vous devez ajouter des dépendances au fichier build.gradle. Mon fichier ressemble à ceci:

buildscript {
	dependencies {
		classpath group: "com.liferay", name: "com.liferay.gradle.plugins.rest.builder", version: "1.0.21"
	}

	repositories {
		maven {
			url "https://repository-cdn.liferay.com/nexus/content/groups/public"
		}
	}
}

apply plugin: "com.liferay.portal.tools.rest.builder"

dependencies {
	compileOnly group: "com.fasterxml.jackson.core", name: "jackson-annotations", version: "2.9.9"
	compileOnly group: "com.liferay", name: "com.liferay.adaptive.media.api"
	compileOnly group: "com.liferay", name: "com.liferay.adaptive.media.image.api"
	compileOnly group: "com.liferay", name: "com.liferay.headless.common.spi"
	compileOnly group: "com.liferay", name: "com.liferay.headless.delivery.api"
	compileOnly group: "com.liferay", name: "com.liferay.osgi.service.tracker.collections"
	compileOnly group: "com.liferay", name: "com.liferay.petra.function"
	compileOnly group: "com.liferay", name: "com.liferay.petra.string"
	compileOnly group: "com.liferay", name: "com.liferay.portal.odata.api"
	compileOnly group: "com.liferay", name: "com.liferay.portal.vulcan.api"
	compileOnly group: "com.liferay", name: "com.liferay.segments.api"
	compileOnly group: "com.liferay.portal", name: "com.liferay.portal.impl"
	compileOnly group: "com.liferay.portal", name: "com.liferay.portal.kernel"
	compileOnly group: "io.swagger.core.v3", name: "swagger-annotations", version: "2.0.5"
	compileOnly group: "javax.portlet", name: "portlet-api"
	compileOnly group: "javax.servlet", name: "javax.servlet-api"
	compileOnly group: "javax.validation", name: "validation-api", version: "2.0.1.Final"
	compileOnly group: "javax.ws.rs", name: "javax.ws.rs-api"
	compileOnly group: "org.osgi", name: "org.osgi.service.component", version: "1.3.0"
	compileOnly group: "org.osgi", name: "org.osgi.service.component.annotations"
	compileOnly group: "org.osgi", name: "org.osgi.core"
	compileOnly project(":modules:headless-vitamins:headless-vitamins-api")
}

Vous n'avez rien à ajouter au fichier bnd.bnd car tous les packages sont internes.

Résumé

Vous avez atteint le point où vous pouvez commencer à construire votre implémentation! Cette fois, je vais interrompre.

Dans la Partie 1, nous avons créé un projet, défini des composants réutilisables et abordé OpenAPI Yaml. C'était. Dans Partie 2, ajoutez toutes les définitions de chemin pour le service OpenAPI et utilisez REST Builder pour coder Généré. Dans la partie 3 (cet article), nous avons examiné tout le code généré et réalisé que nous n'avions pas à nous soucier des changements de code ou des annotations de code d'implémentation.

Enfin, dans la dernière partie suivante, nous avons ajouté le module Service Builder au projet pour le stockage de données, et tout Implémentez la méthode de ressource et utilisez le code ServiceBuilder.

À la prochaine!

https://github.com/dnebing/vitamins

Recommended Posts

Comment créer votre propre API headless à l'aide de REST Builder de Liferay (partie 3)
Comment créer votre propre API headless à l'aide de REST Builder de Liferay (partie 2)
Comment créer votre propre API headless à l'aide de REST Builder de Liferay (partie 4)
Comment créer votre propre API headless à l'aide de REST Builder de Liferay (partie 1)
Comment créer votre propre contrôleur correspondant à / error avec Spring Boot
Comment créer votre propre annotation en Java et obtenir la valeur
Créons une API REST à l'aide de WildFly Swarm.
[Rails] Comment créer un graphique à l'aide de lazy_high_charts
Comment implémenter le verrouillage optimiste dans l'API REST
Comment créer des données de catégorie hiérarchique à l'aide de l'ascendance
Comment créer docker-compose
[Forge] Comment enregistrer votre propre Entité et Entité Render dans 1.13.2
Comment créer une partie d'espace réservé à utiliser dans la clause IN
Comment déployer jQuery dans les applications Rails à l'aide de Webpacker
Comment créer un portlet de générateur de services dans Liferay 7 / DXP
Comment lire un fichier MIDI à l'aide de l'API Java Sound
Comment utiliser l'API Chain
Comment utiliser @Builder (Lombok)
Créez vos propres annotations Java
Introduction à l'API EHRbase 2-REST
Comment créer une méthode
Comment autoriser à l'aide de graphql-ruby
Comment créer un fichier jar et un fichier war à l'aide de la commande jar
[Rails 6] Comment créer un écran de saisie de formulaire dynamique à l'aide de cocoon
Un moyen simple de créer une classe de mappage lors de l'utilisation de l'API