JavaRush /Java Blog /Random-ID /Kami menambahkan kemampuan untuk berlangganan sekelompok ...

Kami menambahkan kemampuan untuk berlangganan sekelompok artikel. (Bagian 1) - "Proyek Java dari A sampai Z"

Dipublikasikan di grup Random-ID
Halo! Hari ini kami akan menambahkan langganan ke grup artikel di JavaRush. Hal ini sesuai dengan masalah JRTB-5 di GitHub. Izinkan saya menjelaskan: JavaRush memiliki bagian yang disebut Articles , dan di dalamnya terdapat Grup Artikel. Idenya adalah untuk menerima pemberitahuan tentang artikel baru dari satu atau lebih grup melalui bot telegram.“Proyek Java dari A hingga Z”: Menambahkan kemampuan untuk berlangganan sekelompok artikel.  Bagian 1 - 1

Tambahkan JRTB-5

Katakanlah saya tertarik dengan artikel dari grup Kisah Sukses . Oleh karena itu, saya ingin berlangganan pembaruan dari grup ini dan menerima tautan ke publikasi baru setiap saat. Sebagai bagian dari tugas ini, kita perlu mempelajari cara menggunakan API terbuka untuk bekerja dengan grup di JavaRush. Tepat pada saat ini, hal seperti itu tiba. Berikut ini tautan ke deskripsi API terbuka .
Teman-teman! Apakah Anda ingin segera mengetahui kapan kode baru untuk suatu proyek atau artikel baru dirilis? Bergabunglah dengan saluran tg saya . Di sana saya mengumpulkan artikel, pemikiran, dan pengembangan sumber terbuka saya bersama-sama.

Apa itu kesombongan? Mari kita cari tahu sekarang

Kami belum membicarakan tentang kesombongan. Bagi yang belum tahu, saya akan jelaskan secara singkat: ini adalah tempat di mana Anda dapat secara terbuka melihat API suatu server dan mencoba membuat beberapa permintaan ke dalamnya. Biasanya, orang yang angkuh mengelompokkan kemungkinan permintaan. Dalam kasus kami, ada tiga grup: forum-question , group , post . Di setiap grup akan ada satu atau lebih permintaan yang menunjukkan semua data yang diperlukan untuk membuat permintaan ini (yaitu, parameter tambahan apa yang dapat diteruskan, apa yang harus dilakukan dengannya, metode http apa, dan sebagainya). Saya menyarankan Anda untuk membaca dan menonton lebih banyak tentang topik ini, karena ini adalah bagian perkembangan yang hampir setiap orang dari Anda akan temui. Untuk mengetahuinya, mari kita cari tahu berapa banyak grup yang ada di JavaRush. Untuk melakukannya, perluas grup pengontrol grup dan pilih Dapatkan permintaan /api/1.0/rest/groups/count . Ini akan mengembalikan kepada kita jumlah grup di JavaRush. Mari kita lihat: “Proyek Java dari A hingga Z”: Menambahkan kemampuan untuk berlangganan sekelompok artikel.  Bagian 1 - 2Gambar menunjukkan bahwa query ini mendukung beberapa parameter (query, type, filter). Untuk mencoba permintaan ini, Anda perlu menemukan tombol Coba , setelah itu parameter berikut dapat dikonfigurasi: “Proyek Java dari A hingga Z”: Menambahkan kemampuan untuk berlangganan sekelompok artikel.  Bagian 1 - 3Anda juga dapat mengonfigurasi jenis, filter, dan kueri di sana (sebenarnya menarik di sini: ini akan menjadi pencarian teks dalam a kelompok). Namun untuk saat ini, mari kita jalankan tanpa batasan apa pun dan lihat berapa banyak grup yang ada di JavaRush. Untuk melakukan ini, klik Jalankan. Tepat di bawah akan ada respons (di bagian Respons Server) terhadap permintaan ini: “Proyek Java dari A hingga Z”: Menambahkan kemampuan untuk berlangganan sekelompok artikel.  Bagian 1 - 4Kami melihat ada total 30 grup , permintaan ini diselesaikan dalam 95 md dan ada serangkaian header dalam responsnya. Selanjutnya, mari kita coba mengonfigurasi beberapa parameter. Mari kita pilih parameter tipe yang sama dengan nilai PERUSAHAAN dan lihat bagaimana hasilnya berubah: “Proyek Java dari A hingga Z”: Menambahkan kemampuan untuk berlangganan sekelompok artikel.  Bagian 1 - 5Ada 4. Bagaimana cara memeriksanya? Caranya mudah: Anda dapat mengunjungi situs webnya, mencari bagian artikel, memilih semua grup dan menambahkan filter yang sesuai di sana ( https://javarush.com/groups/all?type=COMPANY ). Dan iya memang hanya ada 4 saja, padahal sebenarnya ada tiga :D “Proyek Java dari A hingga Z”: Menambahkan kemampuan untuk berlangganan sekelompok artikel.  Bagian 1 - 6Sejauh ini cocok. Ngomong-ngomong, kalau kita cek di universitas-universitas, belum ada. Sekadar iseng, lihat apa yang terjadi jika Anda menyetel filter = MY di browser tempat Anda masuk ke Javarush dan tidak masuk. Lebih lanjut tentang kesombongan - dalam artikel di Habré ini .

Menulis klien untuk Javarush API untuk grup

Sekarang, berdasarkan API terbuka, kita akan menulis klien Java yang dapat membuat permintaan, menerima respons, dan mengetahui secara pasti objek apa yang akan tiba. Kami juga akan mengambil objek dari kesombongan, dari bagian Model (di bagian paling bawah halaman). Mari buat paket baru dan beri nama javarushclient di sebelah layanan, repositori. Di masa depan, kami akan memindahkan ini ke perpustakaan terpisah dalam organisasi Komunitas Javarush dan akan menggunakannya secara eksklusif sebagai ketergantungan. Pertama-tama, Anda perlu menambahkan Unitrest, perpustakaan untuk membuat permintaan http ke JavaRush API:
<dependency>
  <groupId>com.konghq</groupId>
  <artifactId>unirest-java</artifactId>
  <version>${unirest.version}</version>
</dependency>
Dan letakkan versinya di blok properti:
<unirest.version>3.11.01</unirest.version>
Setelah kita memiliki ketergantungan, kita dapat mulai menambahkan kode. Mari buat klien untuk grup JavaRushGroupClient dan implementasi di kelas JavaRushGroupClientImpl. Tapi pertama-tama Anda perlu membuat DTO (objek transfer data) - yaitu kelas yang objeknya akan membawa semua data yang diperlukan untuk klien. Semua model dapat dilihat dengan angkuh.Di bagian paling bawah terdapat bagian Model , di mana Anda dapat menghitungnya. Inilah tampilan GroupDiscussionInfo di swagger: Dalam paket javarushclient, kita akan membuat “Proyek Java dari A hingga Z”: Menambahkan kemampuan untuk berlangganan sekelompok artikel.  Bagian 1 - 7paket dto , yang akan kita tambahkan, berdasarkan data dari swagger, kelas-kelas berikut:
  • Status Info Grup Saya :

    package com.github.javarushcommunity.jrtb.javarushclient.dto;
    
    /**
    * Member group status.
    */
    public enum MeGroupInfoStatus {
       UNKNOWN, CANDIDATE, INVITEE, MEMBER, EDITOR, MODERATOR, ADMINISTRATOR, BANNED
    }

  • Info Grup Saya :

    package com.github.javarushcommunity.jrtb.javarushclient.dto;
    
    import lombok.Data;
    
    /**
    * Group information related to authorized user. If there is no user - will be null.
    */
    @Data
    public class MeGroupInfo {
       private MeGroupInfoStatus status;
       private Integer userGroupId;
    }

  • Tipe Info Grup :

    package com.github.javarushcommunity.jrtb.javarushclient.dto;
    
    /**
    * Group Info type;
    */
    public enum GroupInfoType {
       UNKNOWN, CITY, COMPANY, COLLEGE, TECH, SPECIAL, COUNTRY
    }

  • Info Diskusi Pengguna :

    package com.github.javarushcommunity.jrtb.javarushclient.dto;
    
    import lombok.Data;
    
    /**
    * DTO for User discussion info.
    */
    @Data
    public class UserDiscussionInfo {
    
       private Boolean isBookmarked;
       private Integer lastTime;
       private Integer newCommentsCount;
    }

  • Status Visibilitas Grup :

    package com.github.javarushcommunity.jrtb.javarushclient.dto;
    
    /**
    * Group Visibility status.
    */
    public enum GroupVisibilityStatus {
       UNKNOWN, RESTRICTED, PUBLIC, PROTECTED, PRIVATE, DISABLED, DELETED
    }

  • Kemudian - Info Grup :

    package com.github.javarushcommunity.jrtb.javarushclient.dto;
    
    import lombok.Data;
    import lombok.ToString;
    
    /**
    * Group Info DTO class.
    */
    @Data
    @ToString
    public class GroupInfo {
    
       private Integer id;
       private String avatarUrl;
       private String createTime;
       private String description;
       private String key;
       private Integer levelToEditor;
       private MeGroupInfo meGroupInfo;
       private String pictureUrl;
       private String title;
       private GroupInfoType type;
       private Integer userCount;
       private GroupVisibilityStatus visibilityStatus;
    }

Karena GroupInfo dan GroupDiscussionInfo hampir sepenuhnya sama, mari kita tautkan keduanya dalam warisan - GroupDiscusionInfo :
package com.github.javarushcommunity.jrtb.javarushclient.dto;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

/**
* Group discussion info class.
*/
@EqualsAndHashCode(callSuper = true)
@Data
@ToString(callSuper = true)
public class GroupDiscussionInfo extends GroupInfo {

   private UserDiscussionInfo userDiscussionInfo;
   private Integer commentsCount;
}
Kami juga memerlukan filter untuk permintaan GroupFilter :
package com.github.javarushcommunity.jrtb.javarushclient.dto;

/**
* Filters for group requests.
*/
public enum GroupFilter {

   UNKNOWN, MY, ALL
}
Dalam permintaan untuk mendapatkan ID, ia mengembalikan GroupDiscussionInfo, dan dalam permintaan kumpulan grup, Anda bisa mendapatkan GroupInfo dan GroupDiscussionInfo. Karena permintaan dapat memiliki tipe, kueri, filter, offset, dan batas, mari buat kelas GroupRequestArgs terpisah dan jadikan sebagai kelas pembuat (baca apa pola pembuatnya):
package com.github.javarushcommunity.jrtb.javarushclient.dto;

import lombok.*;

import java.util.HashMap;
import java.util.Map;

import static java.util.Objects.nonNull;

/**
* Request arguments for group requests.
*/
@Builder
@Getter
public class GroupRequestArgs {

   private final String query;
   private final GroupInfoType type;
   private final GroupFilter filter;

   /**
    * specified where to start getting groups
    */
   private final Integer offset;
   /**
    * Limited number of groups.
    */
   private final Integer limit;

   public Map populateQueries() {
       Map queries = new HashMap<>();
       if(nonNull(query)) {
           queries.put("query", query);
       }
       if(nonNull(type)) {
           queries.put("type", type);
       }
       if(nonNull(filter)) {
           queries.put("filter", filter);
       }
       if(nonNull(offset)) {
           queries.put("offset", offset);
       }
       if(nonNull(limit)) {
           queries.put("limit", limit);
       }
       return queries;
   }
}
Untuk pencarian jumlah grupnya sedikit berbeda. Itu hanya memiliki kueri, tipe dan filter. Dan sepertinya Anda tidak ingin menduplikasi kode tersebut. Pada saat yang sama, jika Anda mulai menggabungkannya, hasilnya akan jelek saat bekerja dengan pembangun. Jadi saya memutuskan untuk memisahkannya dan mengulangi kodenya. Seperti inilah tampilan GroupCountRequestArgs :
package com.github.javarushcommunity.jrtb.javarushclient.dto;

import lombok.Builder;
import lombok.Getter;

import java.util.HashMap;
import java.util.Map;

import static java.util.Objects.nonNull;

/**
* Request arguments for group count requests.
*/
@Builder
@Getter
public class GroupsCountRequestArgs {
   private final String query;
   private final GroupInfoType type;
   private final GroupFilter filter;

   public Map populateQueries() {
       Map queries = new HashMap<>();
       if (nonNull(query)) {
           queries.put("query", query);
       }
       if (nonNull(type)) {
           queries.put("type", type);
       }
       if (nonNull(filter)) {
           queries.put("filter", filter);
       }
       return queries;
   }
}
Ya, saya tidak menyebutkan bahwa dua kelas terakhir memiliki metode populateQueries, yang akan menyiapkan peta untuk membuat kueri (Anda akan melihatnya nanti). Berdasarkan kelas yang dijelaskan di atas, mari buat antarmuka untuk JavaRushGroupClient :
package com.github.javarushcommunity.jrtb.javarushclient;

import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupDiscussionInfo;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupInfo;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupRequestArgs;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupsCountRequestArgs;

import java.util.List;

/**
 * Client for Javarush Open API corresponds to Groups.
 */
public interface JavaRushGroupClient {

    /**
     * Get all the {@link GroupInfo} filtered by provided {@link GroupRequestArgs}.
     *
     * @param requestArgs provided {@link GroupRequestArgs}.
     * @return the collection of the {@link GroupInfo} objects.
     */
    List<GroupInfo> getGroupList(GroupRequestArgs requestArgs);

    /**
     * Get all the {@link GroupDiscussionInfo} filtered by provided {@link GroupRequestArgs}.
     *
     * @param requestArgs provided {@link GroupRequestArgs}
     * @return the collection of the {@link GroupDiscussionInfo} objects.
     */
    List<GroupDiscussionInfo> getGroupDiscussionList(GroupRequestArgs requestArgs);

    /**
     * Get count of groups filtered by provided {@link GroupRequestArgs}.
     *
     * @param countRequestArgs provided {@link GroupsCountRequestArgs}.
     * @return the count of the groups.
     */
    Integer getGroupCount(GroupsCountRequestArgs countRequestArgs);

    /**
     * Get {@link GroupDiscussionInfo} by provided ID.
     *
     * @param id provided ID.
     * @return {@link GroupDiscussionInfo} object.
     */
    GroupDiscussionInfo getGroupById(Integer id);
}
Dua permintaan berbeda untuk kasus ketika kita ingin menambahkan informasi GroupInfo atau GroupDiscussionInfo. Jika tidak, kueri ini identik, dan satu-satunya perbedaan adalah bahwa di satu kueri, tanda includeDiscussion akan benar, dan di kueri lainnya akan salah. Oleh karena itu, ada 4 metode, bukan tiga. Sekarang mari kita mulai menerapkan:
package com.github.javarushcommunity.jrtb.javarushclient;

import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupDiscussionInfo;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupInfo;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupRequestArgs;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupsCountRequestArgs;
import kong.unirest.GenericType;
import kong.unirest.Unirest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.List;

/**
* Implementation of the {@link JavaRushGroupClient} interface.
*/
@Component
public class JavaRushGroupClientImpl implements JavaRushGroupClient {

   private final String javarushApiGroupPath;

   public JavaRushGroupClientImpl(@Value("${javarush.api.path}") String javarushApi) {
       this.javarushApiGroupPath = javarushApi + "/groups";
   }

   @Override
   public List<GroupInfo> getGroupList(GroupRequestArgs requestArgs) {
       return Unirest.get(javarushApiGroupPath)
               .queryString(requestArgs.populateQueries())
               .asObject(new GenericType<list<GroupInfo>>() {
               })
               .getBody();
   }

   @Override
   public List<GroupDiscussionInfo> getGroupDiscussionList(GroupRequestArgs requestArgs) {
       return Unirest.get(javarushApiGroupPath)
               .queryString(requestArgs.populateQueries())
               .asObject(new GenericType<list<GroupDiscussionInfo>>() {
               })
               .getBody();
   }

   @Override
   public Integer getGroupCount(GroupsCountRequestArgs countRequestArgs) {
       return Integer.valueOf(
               Unirest.get(String.format("%s/count", javarushApiGroupPath))
                       .queryString(countRequestArgs.populateQueries())
                       .asString()
                       .getBody()
       );
   }

   @Override
   public GroupDiscussionInfo getGroupById(Integer id) {
       return Unirest.get(String.format("%s/group%s", javarushApiGroupPath, id.toString()))
               .asObject(GroupDiscussionInfo.class)
               .getBody();
   }


}
Saya menambahkan jalur ke API di konstruktor menggunakan anotasi Nilai yang sudah familiar. Ini menyiratkan bahwa nilai di dalam anotasi sesuai dengan bidang di file properti. Oleh karena itu, mari tambahkan baris baru ke application.properties:
javarush.api.path=https://javarush.com/api/1.0/rest
Nilai ini sekarang akan berada di satu tempat untuk semua klien API, dan jika jalur API berubah, kami akan segera memperbaruinya. Sebelumnya, saya memalu paku dengan mikroskop, menerima respons dari permintaan http melalui Unirest, menerjemahkannya ke dalam string dan kemudian menguraikan string ini melalui Jackson... Itu menakutkan, membosankan dan membutuhkan banyak hal tambahan. Di perpustakaan ini Anda dapat melihat seperti apa. Segera setelah saya mendapatkannya, saya akan memfaktorkan ulang semuanya.
Siapa pun yang ingin mencoba memperbarui perpustakaan ini - tambahkan objek penerima hanya menggunakan alat perpustakaan yang tidak ada - tulis dalam pesan pribadi atau sebagai terbitan baru di perpustakaan itu sendiri. Ini akan menjadi pengalaman kerja nyata bagi Anda, tapi saya tidak keberatan. Saya akan melakukan peninjauan kode lengkap dan membantu jika perlu.
Sekarang pertanyaannya adalah: apakah kode kita berfungsi seperti yang kita harapkan? Jawabannya mudah: Anda hanya perlu menulis tes untuknya. Seperti yang telah saya katakan lebih dari sekali, pengembang harus bisa menulis tes. Oleh karena itu, dengan menggunakan UI Swagger kami, kami akan mengirimkan permintaan, melihat tanggapannya dan menggantinya ke dalam pengujian sebagai hasil yang diharapkan. Anda mungkin langsung menyadari bahwa jumlah grup tidak statis dan dapat berubah. Dan Anda benar. Satu-satunya pertanyaan adalah seberapa sering angka ini berubah? Sangat jarang, sehingga selama beberapa bulan kita dapat mengatakan bahwa nilai ini akan menjadi statis. Dan jika ada perubahan, kami akan memperbarui tesnya. Bertemu - JavaRushGroupClientTest:
package com.github.javarushcommunity.jrtb.javarushclient;

import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupDiscussionInfo;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupInfo;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupRequestArgs;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupsCountRequestArgs;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.List;

import static com.github.javarushcommunity.jrtb.javarushclient.dto.GroupInfoType.TECH;

@DisplayName("Integration-level testing for JavaRushGroupClientImplTest")
class JavaRushGroupClientTest {

   private final JavaRushGroupClient groupClient = new JavaRushGroupClientImpl("https://javarush.com/api/1.0/rest");

   @Test
   public void shouldProperlyGetGroupsWithEmptyArgs() {
       //given
       GroupRequestArgs args = GroupRequestArgs.builder().build();

       //when
       List<GroupInfo> groupList = groupClient.getGroupList(args);

       //then
       Assertions.assertNotNull(groupList);
       Assertions.assertFalse(groupList.isEmpty());
   }

   @Test
   public void shouldProperlyGetWithOffSetAndLimit() {
       //given
       GroupRequestArgs args = GroupRequestArgs.builder()
               .offset(1)
               .limit(3)
               .build();

       //when
       List<GroupInfo> groupList = groupClient.getGroupList(args);

       //then
       Assertions.assertNotNull(groupList);
       Assertions.assertEquals(3, groupList.size());
   }

   @Test
   public void shouldProperlyGetGroupsDiscWithEmptyArgs() {
       //given
       GroupRequestArgs args = GroupRequestArgs.builder().build();

       //when
       List<GroupDiscussionInfo> groupList = groupClient.getGroupDiscussionList(args);

       //then
       Assertions.assertNotNull(groupList);
       Assertions.assertFalse(groupList.isEmpty());
   }

   @Test
   public void shouldProperlyGetGroupDiscWithOffSetAndLimit() {
       //given
       GroupRequestArgs args = GroupRequestArgs.builder()
               .offset(1)
               .limit(3)
               .build();

       //when
       List<GroupDiscussionInfo> groupList = groupClient.getGroupDiscussionList(args);

       //then
       Assertions.assertNotNull(groupList);
       Assertions.assertEquals(3, groupList.size());
   }

   @Test
   public void shouldProperlyGetGroupCount() {
       //given
       GroupsCountRequestArgs args = GroupsCountRequestArgs.builder().build();

       //when
       Integer groupCount = groupClient.getGroupCount(args);

       //then
       Assertions.assertEquals(30, groupCount);
   }

   @Test
   public void shouldProperlyGetGroupTECHCount() {
       //given
       GroupsCountRequestArgs args = GroupsCountRequestArgs.builder()
               .type(TECH)
               .build();

       //when
       Integer groupCount = groupClient.getGroupCount(args);

       //then
       Assertions.assertEquals(7, groupCount);
   }

   @Test
   public void shouldProperlyGetGroupById() {
       //given
       Integer androidGroupId = 16;

       //when
       GroupDiscussionInfo groupById = groupClient.getGroupById(androidGroupId);

       //then
       Assertions.assertNotNull(groupById);
       Assertions.assertEquals(16, groupById.getId());
       Assertions.assertEquals(TECH, groupById.getType());
       Assertions.assertEquals("android", groupById.getKey());
   }
}
Tes ditulis dengan gaya yang sama seperti sebelumnya. Ada beberapa tes untuk setiap permintaan. Tidak ada gunanya menguji semuanya, karena menurut saya API ini telah diuji dengan cara terbaik.

Kesimpulan

Sebagai bagian dari artikel ini, kami menambahkan klien Java untuk grup ke JavaRush API. Seperti kata pepatah, hidup dan belajar. Saat saya menulis klien ini, saya memanfaatkan dokumentasi mereka dan dengan nyaman menggunakan pekerjaan dengan objek yang mereka sediakan. Saya menarik perhatian Anda pada tugas yang saya usulkan. Jika ada yang tertarik, kirimkan pesan pribadi kepada saya, saya yakin ini akan menjadi pengalaman yang sangat menarik. Ini adalah bagian pertama. Yang kedua, kita akan langsung mengimplementasikan perintah penambahan dan (jika kita memasukkannya ke dalam satu artikel) kita akan menambahkan daftar grup tempat pengguna berlangganan. Selanjutnya, siapa pun yang memiliki keinginan dan bakat menulis teks untuk bot, silakan menulis kepada saya di PM. Saya bukan ahli dalam hal ini dan bantuan apa pun akan sangat membantu. Mari kita formalkan semua ini sebagai pengembangan open source, ini akan menarik! Ya, seperti biasa - suka, berlangganan, bel , beri bintang pada proyek kami , tulis komentar, dan beri peringkat artikel!
tautan yang bermanfaat

Daftar semua materi dalam seri ini ada di awal artikel ini.

Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION