Create Project - Developer

Asset Publisher

null Create Project
از آنجا که این سمپل شامل مبحث مهم استفاده از headless در برنامه‌نویسی پرتلت می‌باشد، لازم است همراه با مطالعه آن اقدامات زیر نیز انجام شود:

۱) توجه داشته باشید که مراجع معرفی شده لاتین مبنای کار است و در آن توضیحات کافی در مورد هر بخش آمده است. نکات فارسی که بعدا به متن اضافه شده، برای کمک به رفع اشکالات ریز و یادگیری بهتر است. بنابراین ضروری است که مطالعه منابع لاتین با دقت انجام شود.

۲) پروژه را روی github قرار دهید و برای هر مرحله، یک branch ایجاد شود و پس از اتمام موفقیت آمیز آن مرحله، در branch اصلی، merge شود. ضمنا از tag های مناسب استفاده شود.
نکته: مرجع اصلی این  سمپل، مقاله  David H Nebinger  می‌باشد که آن نیز با توجه به مستندات لایفری در زمینه REST Builder تهیه شده تا مفهوم REST API در لایفری را بهتر بیان کند. بنابراین مطالعه هر دو مقاله لازم است. البته در مستند فارسی حاضر، سعی شده تا برخی نکات و ابهامات، به بیان ساده‌تر در اختیار شما قرار گیرد؛ اما این موضوع، ضرورت مطالعه دو مقاله فوق را برطرف نمی‌کند. سورس مقاله اصلی از  این آدرس در دسترس می‌باشد.

نکته ۲: ممکنست مواردی مغایر مرجع اصلی وجود داشته باشد که باید رفع شود. لطفا در صورت مواجه شدن با چنین مواردی، حتما آن را به مسئول آموزش یادآوری نمایید.

نکته ۳: این راهنما برای لینوکس تهیه شده ولی با اندکی تفاوت می‌توان آن را در ویندوز هم استفاده نمود. به طور مثال بجای استفاده از دستور nano در ترمینال، از یک ویرایشگر متن مثل notepad استفاده کنید.

 

۱) ایجاد پروژه؛  درون ترمینال این دستورات را بزنید.

mkdir vitamins
cd vitamins
blade init

(select or type portal-7.3-ga7 or topper)

در سمپل اصلی ــ که توسط آقای دیوید نبینگر نوشته شده از نسخه پرتال 7.2-ga1 استفاده شده اما به صورت کلی نسخه 7.2 لایفری دچار اشکالاتی است که بهتر است از آن استفاده نشود. بنابراین ما از نسخه 7.3 استفاده می‌کنیم. طبعا فایل gradle.properties نیز با توجه به نسخه لایفری تنظیم می‌شود.

۳)

./gradlew tasks

با اجرای این دستور ، پیامهای مربوطه نمایش داده می‌شود که خطوط ابتدایی و انتهایی آن به صورت زیر است.

Starting a Gradle Daemon (subsequent builds will be faster)
.....
BUILD SUCCESSFUL in 11s
1 actionable task: 1 executed

۴) بررسی فایل gradle.properties:

nano gradle.properties

که مشابه محتوای زیر باشد (با توجه به نسخه انتخاب شده لایفری) :

liferay.workspace.modules.dir=modules
microsoft.translator.subscription.key=
liferay.workspace.product = portal-7.3-ga7

 

۵) بررسی فایل settings.gradle :

nano settings.gradle

محتوای این فایل باید شبیه موارد زیر باشد (البته ممکن است ورژن برخی کلاسها متفاوت باشد).

buildscript {
    dependencies {
        classpath group: "com.liferay", name: "com.liferay.gradle.plugins.workspace", version: "3.4.2"
        classpath group: "net.saliman", name: "gradle-properties-plugin", version: "1.4.6"
    }

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

apply plugin: "net.saliman.properties"

apply plugin: "com.liferay.workspace"

 

۶) ایجاد پوشه headless به عنوان زیرپوشه modules برای تجمیع ماژولهای مربوط به headless و سپس ایجاد ۴  زیر ماژول برای آن :

mkdir modules/headless-vitamins
cd modules/headless-vitamins
blade create -t api -p com.dnebinger.headless.vitamins headless-vitamins-api
blade create -t api -p com.dnebinger.headless.vitamins headless-vitamins-impl
blade create -t api -p com.dnebinger.headless.vitamins headless-vitamins-client
blade create -t api -p com.dnebinger.headless.vitamins headless-vitamins-test

blade create -t api -p com.dnebinger.headless.vitamins headless-vitamins-cli

۷) تغییر نام پوشه main ( از زیر پوشه src درر ماژول  headless-vitamins-test ) به  testIntegration:

mv headless-vitamins-test/src/main headless-vitamins-test/src/testIntegration

۸) افزودن فایل rest-config.yaml  به ماژول  headless-vitamins-impl  :

nano headless-vitamins-impl/rest-config.yaml

با محتوای زیر :

apiDir: "../headless-vitamins-api/src/main/java"
apiPackagePath: "com.dnebinger.headless.vitamins"
application:
  baseURI: "/headless-vitamins"
  className: "HeadlessVitaminsApplication"
  name: "dnebinger.Headless.Vitamins"
author: "Dave Nebinger"
clientDir: "../headless-vitamins-client/src/main/java"
testDir: "../headless-vitamins-test/src/testIntegration/java"

 

۹) افزودن فایل rest-openapi.yaml :

nano headless-vitamins-impl/rest-openapi.yaml

با  محتوای زیر  :

components:
  schemas:
    Vitamin:
      description: Contains all of the data for a single vitamin or mineral.
      properties:
        name:
          description: The vitamin or mineral name.
          type: string
        id:
          description: The vitamin or mineral internal ID.
          type: string
        chemicalNames:
          description: The chemical names of the vitamin or mineral if it has some.
          items:
            type: string
          type: array
        properties:
          description: The chemical properties of the vitamin or mineral if it has some.
          items:
            type: string
          type: array
        group:
          description: The group the vitamin or mineral belongs to.
          type: string
        description:
          description: The description of the vitamin or mineral.
          type: string
        articleId:
          description: A journal articleId if there is a web content article for this vitamin.
          type: string
        type:
          description: The type of the vitamin or mineral.
          enum: [Vitamin, Mineral, Other]
          type: string
        attributes:
          description: Health properties attributed to the vitamin or mineral.
          items:
            type: string
          type: array
        risks:
          description: Risks associated with the vitamin or mineral.
          items:
            type: string
          type: array
        symptoms:
          description: Symptoms associated with the vitamin or mineral deficiency.
          items:
            type: string
          type: array
        creator:
          $ref: "#/components/schemas/Creator"
      type: object
    Creator:
      description: Represents the user account of the content's creator/author. Properties follow the [creator](https://schema.org/creator) specification.
      properties:
        additionalName:
          description: The author's additional name (e.g., middle name).
          readOnly: true
          type: string
        familyName:
          description: The author's surname.
          readOnly: true
          type: string
        givenName:
          description: The author's first name.
          readOnly: true
          type: string
        id:
          description: The author's ID.
          format: int64
          readOnly: true
          type: integer
        image:
          description: A relative URL to the author's profile image.
          format: uri
          readOnly: true
          type: string
        name:
          description: The author's full name.
          readOnly: true
          type: string
        profileURL:
          description: A relative URL to the author's user profile.
          format: uri
          readOnly: true
          type: string
      type: object
openapi: 3.0.1
info:
  title: "Headless Vitamins"
  version: v1.0
  description: "API for accessing Vitamin details."
paths:
  "/vitamins":
    get:
      operationId: getVitaminsPage
      tags: ["Vitamin"]
      description: Retrieves the list of vitamins and minerals. Results can be paginated, filtered, searched, and sorted.
      parameters:
        - in: query
          name: filter
          schema:
            type: string
        - in: query
          name: page
          schema:
            type: integer
        - in: query
          name: pageSize
          schema:
            type: integer
        - in: query
          name: search
          schema:
            type: string
        - in: query
          name: sort
          schema:
            type: string
      responses:
        200:
          description: ""
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/Vitamin"
                type: array
            application/xml:
              schema:
                items:
                  $ref: "#/components/schemas/Vitamin"
                type: array
    post:
      operationId: postVitamin
      tags: ["Vitamin"]
      description: Create a new vitamin/mineral.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Vitamin"
          application/xml:
            schema:
              $ref: "#/components/schemas/Vitamin"
      responses:
        200:
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Vitamin"
            application/xml:
              schema:
                $ref: "#/components/schemas/Vitamin"
  "/vitamins/{vitaminId}":
    get:
      operationId: getVitamin
      tags: ["Vitamin"]
      description: Retrieves the vitamin/mineral via its ID.
      parameters:
        - name: vitaminId
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Vitamin"
            application/xml:
              schema:
                $ref: "#/components/schemas/Vitamin"
    put:
      operationId: putVitamin
      tags: ["Vitamin"]
      description: Replaces the vitamin/mineral with the information sent in the request body. Any missing fields are deleted, unless they are required.
      parameters:
        - name: vitaminId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Vitamin"
          application/xml:
            schema:
              $ref: "#/components/schemas/Vitamin"
      responses:
        200:
          description: Default Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Vitamin"
            application/xml:
              schema:
                $ref: "#/components/schemas/Vitamin"
    patch:
      operationId: patchVitamin
      tags: ["Vitamin"]
      description: Replaces the vitamin/mineral with the information sent in the request body. Any missing fields are deleted, unless they are required.
      parameters:
        - name: vitaminId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Vitamin"
          application/xml:
            schema:
              $ref: "#/components/schemas/Vitamin"
      responses:
        200:
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Vitamin"
            application/xml:
              schema:
                $ref: "#/components/schemas/Vitamin"
    delete:
      operationId: deleteVitamin
      tags: ["Vitamin"]
      description: Deletes the vitamin/mineral and returns a 204 if the operation succeeds.
      parameters:
        - name: vitaminId
          in: path
          required: true
          schema:
            type: string
      responses:
        204:
          description: ""
          content:
            application/json: {}

توجه : در مستند بعدی، جزییات فایل rest-openapi.yaml تشریح شده است.

 

۱۰) بررسی نتایج :
برای بررسی میزان صحت کدهای فایل rest-openapi.yaml این کدها را در ادیتور Swagger قرار می‌دهیم (در کادر سمت چپ آن paste می‌کنیم) . این ادیتور هم context-sensitive  است و هم موارد خطا را بلافاصله منعکس می‌کند.

نکته ۱: این ادیتور خطاهای syntax را تشخیص می‌دهد نه خطاهای منطقی را.
نکته ۲: مواردی هست که syntax کد فایل yaml در این ادیتتور مشکلی نداشته اما موقع کامپایل (build rest) با خطا مواجه شده است.

تصویر ۱: اجرای کد فایل rest-openapi.yaml در ادیتور Swagger

 

اگر کد فوق مشکلی داشته باشد، خطایی شبیه خطای زیر نمایش داده می‌شود :

تصویر ۲: خطای کد فایل rest-openapi.yaml در ادیتور Swagger

 

۱۱)  اصلاح build.gradle ماژول headless-vitamins-impl :
اکنون آماده هستیم تا REST Builder جدید خود را فراخوانی کنیم اما قبل از آن ، با دستور

nano headless-vitamins-impl/build.gradle

۱۲) فایل build.gradle  را به صورت زیر تغییر دهید  :

buildscript {
    dependencies {
           //compileOnly group: "com.liferay.portal", name: "release.portal.api"
           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")
    //compileOnly project(":modules:vitamins:vitamins-api")
    compileOnly group: "org.slf4j", name: "slf4j-api"

    restBuilder group: "com.liferay", name: "com.liferay.portal.tools.rest.builder", version: "1.0.23"
}

 

نکته: ورژن برخی dependency های فوق متناسب با لایفری 7.2 است. در صورتی که پروژه را با لایفری 7.3 یا بالاتر ایجاد کرده باشید، ممکنست این ورژنها نیاز به تغییر داشته باشد.

 

۱۳) سپس در ترمینال دستور زیر را بزنید:

$ ../../gradlew buildREST

نکته مهم : در کدهای فایل rest-openapi.yaml درون تگ parameters  خط حاوی name باید زیر خط in:path قرار گیرد. عدم اصلاح این باعث بروز خطا خواهد شد.

اگر همه چیز درست باشد باید این پیام را دریافت کنید :

BUILD SUCCESSFUL in ...s
1 actionable task: 1 executed

 

فهرست منابع :

ردیفمنابعنوع محتوا
۱Generating APIs with REST Builderمتن
۲Creating Headless api Part#1 (by David H Nebinger)متن
۳REST Builderمتن
۴Deploy an Example REST APIمتن