JavaRush /בלוג Java /Random-HE /אנו מוסיפים את האפשרות להירשם לקבוצת מאמרים. (חלק 1) - "פ...
Roman Beekeeper
רָמָה

אנו מוסיפים את האפשרות להירשם לקבוצת מאמרים. (חלק 1) - "פרויקט ג'אווה מא' עד ת'"

פורסם בקבוצה
שלום! היום נוסיף מנוי לקבוצת מאמרים ב-JavaRush. זה מתאים לנושא JRTB-5 ב- GitHub. תן לי להסביר: ל-JavaRush יש קטע שנקרא מאמרים , ובו יש קבוצות מאמרים. הרעיון הוא לקבל הודעות על מאמר חדש מקבוצה אחת או יותר באמצעות בוט טלגרם."פרויקט ג'אווה מא' עד ת'": הוספת אפשרות להירשם לקבוצת מאמרים.  חלק 1 - 1

הוסף JRTB-5

נניח שאני מתעניין במאמרים מקבוצת סיפורי הצלחה . לכן אני רוצה להירשם לעדכונים מקבוצה זו ולקבל בכל פעם קישור לפרסום חדש. כחלק ממשימה זו, עלינו ללמוד כיצד להשתמש ב-API הפתוח לעבודה עם קבוצות ב-JavaRush. בדיוק ברגע הזה הגיע דבר כזה. הנה קישור לתיאור של ה-API הפתוח .
חברים! האם אתה רוצה לדעת מיד מתי יוצא קוד חדש לפרויקט או מאמר חדש? הצטרף לערוץ ה-tg שלי . שם אני אוסף את המאמרים, המחשבות והפיתוח שלי בקוד פתוח ביחד.

מה זה סוואגר? בוא נבין את זה עכשיו

עוד לא דיברנו על השטות. למי שלא יודע, אסביר בקצרה: זה מקום שבו אתה יכול להסתכל בגלוי על ה-API של שרת ולנסות לשלוח אליו כמה בקשות. בדרך כלל, מאגר מקבץ בקשות אפשריות. במקרה שלנו, יש שלוש קבוצות: פורום-שאלה , קבוצה , פוסט . בכל קבוצה תהיה בקשה אחת או יותר המציינת את כל הנתונים הדרושים לבניית בקשה זו (כלומר אילו פרמטרים נוספים ניתן להעביר, מה לעשות איתם, איזו שיטת http וכו'). אני ממליץ לכם לקרוא ולצפות יותר בנושא זה, כי זה החלק של הפיתוח שכמעט כל אחד מכם יתקל בו. כדי להבין את זה, בואו לגלות כמה קבוצות יש ב-JavaRush. כדי לעשות זאת, הרחב את קבוצת בקרי הקבוצה ובחר קבל בקשה /api/1.0/rest/groups/count . זה יחזיר לנו את מספר הקבוצות ב-JavaRush. בואו נסתכל: "פרויקט ג'אווה מא' עד ת'": הוספת אפשרות להירשם לקבוצת מאמרים.  חלק 1 - 2התמונה מראה שהשאילתה הזו תומכת במספר פרמטרים (שאילתה, סוג, מסנן). כדי לנסות את הבקשה הזו, אתה צריך למצוא את כפתור נסה את זה , ולאחר מכן ניתן להגדיר את הפרמטרים הבאים: "פרויקט ג'אווה מא' עד ת'": הוספת אפשרות להירשם לקבוצת מאמרים.  חלק 1 - 3אתה יכול גם להגדיר את הסוג, המסנן והשאילתה שם (זה בעצם מעניין כאן: זה יהיה חיפוש טקסט ב- קְבוּצָה). אבל לעת עתה, בואו נריץ את זה ללא הגבלות ונראה כמה קבוצות יש ב-JavaRush. כדי לעשות זאת, לחץ על ביצוע. ממש מתחת תהיה תגובה (בקטע Server Response) לבקשה הזו: "פרויקט ג'אווה מא' עד ת'": הוספת אפשרות להירשם לקבוצת מאמרים.  חלק 1 - 4אנו רואים שיש 30 קבוצות בסך הכל , בקשה זו הושלמה ב-95ms ויש קבוצה של כמה כותרות בתגובה. לאחר מכן, בואו ננסה להגדיר כמה פרמטרים. בוא נבחר את פרמטר הסוג השווה לערך COMPANY ונראה איך התוצאה משתנה: "פרויקט ג'אווה מא' עד ת'": הוספת אפשרות להירשם לקבוצת מאמרים.  חלק 1 - 5יש 4 מהם איך בודקים את זה? זה קל: אתה יכול להיכנס לאתר, למצוא את קטע המאמרים, לבחור את כל הקבוצות ולהוסיף שם את המסנן המתאים ( https://javarush.com/groups/all?type=COMPANY ). וכן, אכן, יש רק 4 מהם. למרות שבעצם יש שלושה :D "פרויקט ג'אווה מא' עד ת'": הוספת אפשרות להירשם לקבוצת מאמרים.  חלק 1 - 6עד כאן זה מתאים. אגב, אם נבדוק את האוניברסיטאות, אין כאלה עדיין. סתם בשביל הכיף, ראה מה קורה אם אתה מגדיר filter = MY בדפדפן שבו אתה מחובר ל-Javarush ולא מחובר. עוד על השטות - במאמר זה על Habré .

כתיבת לקוח עבור Javarush API לקבוצות

כעת, בהתבסס על ה-API הפתוח, נכתוב לקוח Java שיכול לבקש בקשות, לקבל תגובות ויודע בדיוק אילו אובייקטים יגיעו. ניקח גם חפצים מהסוואגר, ממדור הדגמים (בתחתית העמוד). בואו ניצור חבילה חדשה ונקרא לה javarushclient ליד service, repository. בעתיד, נעביר את זה לספרייה נפרדת בתוך ארגון Javarush Community ונשתמש בה באופן בלעדי כתלות. קודם כל, עליך להוסיף את Unitrest, ספרייה ליצירת בקשות http ל-JavaRush API:
<dependency>
  <groupId>com.konghq</groupId>
  <artifactId>unirest-java</artifactId>
  <version>${unirest.version}</version>
</dependency>
ושם את הגרסה בבלוק המאפיינים:
<unirest.version>3.11.01</unirest.version>
ברגע שיש לנו תלות, נוכל להתחיל להוסיף קוד. בואו ניצור לקוח לקבוצות JavaRushGroupClient ויישום במחלקה JavaRushGroupClientImpl. אבל קודם צריך ליצור DTOs (אובייקטי העברת נתונים) - כלומר מחלקות שהאובייקטים שלהן ישאו את כל הנתונים הדרושים ללקוח. ניתן לראות את כל הדגמים בסוויץ', ממש בתחתית יש קטע דגמים , בו ניתן לספור אותם. כך נראה GroupDiscussionInfo ב-swagger: בחבילת javarushclient, ניצור "פרויקט ג'אווה מא' עד ת'": הוספת אפשרות להירשם לקבוצת מאמרים.  חלק 1 - 7חבילת dto , אליה נוסיף, על סמך נתונים מה-swagger, את המחלקות הבאות:
  • MeGroupInfoStatus :

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

  • MeGroupInfo :

    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;
    }

  • GroupInfoType :

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

  • UserDiscussionInfo :

    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;
    }

  • סטטוס GroupVisibility :

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

  • לאחר מכן - GroupInfo :

    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;
    }

מכיוון ש- GroupInfo ו- GroupDiscussionInfo זהים כמעט לחלוטין, בואו נקשר אותם בירושה - GroupDiscussionInfo :
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;
}
נצטרך גם מסנן עבור בקשת מסנן קבוצות :
package com.github.javarushcommunity.jrtb.javarushclient.dto;

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

   UNKNOWN, MY, ALL
}
בבקשה לקבל תעודה מזהה, הוא מחזיר את GroupDiscussionInfo, ובבקשה לאוסף קבוצות, ניתן לקבל גם GroupInfo וגם GroupDiscussionInfo. מכיוון שלבקשות יכולות להיות סוג, שאילתה, פילטר, קיזוז ומגבלה, בואו ניצור מחלקה נפרדת של GroupRequestArgs ונהפוך אותה למחלקת בונה (קרא מהי דפוס הבנאים):
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;
   }
}
עבור חיפוש מספר הקבוצות, זה מעט שונה. יש לו רק שאילתה, סוג ומסנן. ונראה שאתה לא רוצה לשכפל את הקוד. יחד עם זאת, אם אתה מתחיל לשלב אותם, זה יוצא מכוער כשעובדים עם בונים. אז החלטתי להפריד ביניהם ולחזור על הקוד. כך נראה 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;
   }
}
כן, לא ציינתי שלשתי המחלקות האחרונות יש שיטת populateQueries, שתכין את המפה ליצירת שאילתה (תראה אותה בהמשך). בהתבסס על השיעורים המתוארים לעיל, בואו ניצור ממשק עבור 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);
}
שתי בקשות שונות למקרה שבו אנו רוצים להוסיף מידע GroupInfo או GroupDiscussionInfo. אחרת, הבקשות הללו זהות, וההבדל היחיד יהיה שבאחת הדגל של includeDiscussion יהיה נכון, ובשנייה הוא יהיה שקר. לכן, היו 4 שיטות, לא שלוש. עכשיו בואו נתחיל ליישם:
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();
   }


}
אני מוסיף את הנתיב ל-API בבנאי באמצעות הערת Value המוכרת כבר. זה מרמז שהערך בתוך ההערה מתאים לשדה בקובץ המאפיינים. לכן, בואו נוסיף שורה חדשה ל application.properties:
javarush.api.path=https://javarush.com/api/1.0/rest
ערך זה יהיה כעת במקום אחד עבור כל לקוחות ה-API, ואם נתיב ה-API משתנה, נעדכן אותו במהירות. בעבר תקשתי מסמרים במיקרוסקופ, קיבלתי תשובה מבקשת http דרך Unirest, תרגמתי אותה למחרוזת ואז ניתחתי את המחרוזת הזו דרך ג'קסון... זה היה מפחיד, מייגע ודרש הרבה דברים נוספים. בספרייה זו תוכלו לראות איך זה נראה. ברגע שאשים את ידיי על זה, אני אשנה הכל מחדש.
כל מי שרוצה לנסות לעדכן ספרייה זו - להוסיף אובייקטים מקבלים רק באמצעות הכלים של ספריית unirest - לכתוב בהודעה אישית או כגיליון חדש בספרייה עצמה. זו תהיה ניסיון עבודה אמיתי עבורך, אבל לא אכפת לי. אערוך סקירת קוד מלאה ואעזור במידת הצורך.
כעת השאלה היא: האם הקוד שלנו עובד כפי שאנו מצפים? התשובה קלה: אתה רק צריך לכתוב להם מבחנים. כפי שאמרתי יותר מפעם אחת, מפתחים חייבים להיות מסוגלים לכתוב מבחנים. לכן, באמצעות ממשק המשתמש של Swagger, אנו נשלח בקשות, נבחן את התגובות ונחליף אותן בבדיקות כתוצאה הצפויה. אולי שמתם לב מיד שמספר הקבוצות אינו סטטי ויכול להשתנות. ואתה צודק. השאלה היחידה היא באיזו תדירות מספר זה משתנה? לעתים רחוקות מאוד, ולכן במהלך מספר חודשים אנו יכולים לומר שהערך הזה יהיה סטטי. ואם ישתנה משהו, נעדכן את הבדיקות. הכירו - 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());
   }
}
המבחנים כתובים באותו סגנון כמו קודם. ישנן מספר בדיקות לכל בקשה. אין טעם לבדוק הכל, כי אני חושב שה-API הזה כבר נבדק בצורה הטובה ביותר.

סיכום

כחלק ממאמר זה, הוספנו לקוח Java לקבוצות ל-JavaRush API. כמו שאומרים, חי ותלמד. בזמן שכתבתי את הלקוח הזה, ניצלתי את התיעוד שלהם והשתמשתי בנוחות בעבודה עם חפצים שהם מספקים. אני מפנה את תשומת לבך למשימה שהצעתי. אם מישהו מעוניין שיכתוב לי הודעה פרטית, אני יותר בטוח שזו תהיה חוויה מאוד מעניינת. זה היה החלק הראשון. בשני, ניישם ישירות את פקודת ההוספה ו(אם נתאים אותה למאמר אחד) נוסיף קבלת רשימה של קבוצות אליהן המשתמש נרשם. לאחר מכן, כל מי שיש לו רצון וכישרון לכתוב טקסטים לבוט, נא לכתוב לי ב-PM. אני לא מומחה בעניין הזה וכל עזרה תעזור מאוד. בואו ננסח את כל זה כפיתוח קוד פתוח, זה יהיה מעניין! ובכן, כרגיל - לייק, הירשמו, צלצול , תנו כוכב לפרויקט שלנו , כתבו תגובות ודרגו את המאמר!
קישורים שימושיים

רשימה של כל החומרים בסדרה נמצאת בתחילת מאמר זה.

הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION