Repository: szdc/tiktok-api (ts)

Issue:
Add a method to get a post by its id
**Is your feature request related to a problem? Please describe.**
Currently the only way to get a post's data is to get all posts from a user until the desired post is reached.
The API has a way to get a single post if you know its id which can be useful sometimes.

**Describe the solution you'd like**
The API call is 'aweme/v1/aweme/detail/' with param aweme_id
The result contains the Post in aweme_detail

**Additional context**
There are some minor differences in the data returned by this call compared to the one used in listPosts(), but nothing important is missing. Can provide examples if needed.


Pull request:
feat: add method for retrieving a single post
Closes #68.

Patch:
diff --git a/README.md b/README.md
index 161e8d3..ec500e4 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,7 @@ const api = new TikTokAPI(params, { signURL });
 * [.getUser(id)](#getuserid)
 * [.searchUsers(params)](#searchusersparams)
 * [.getQRCode(id, [schemaType])](#getqrcodeid-schematype)
+* [.getPost(id)](#getpostid)
 * [.listPosts(params)](#listpostsparams)
 * [.listFollowers(params)](#listfollowersparams)
 * [.listFollowing(params)](#listfollowingparams)
@@ -163,6 +164,22 @@ api.getQRCode('<user_id>')
 
 See the [QR code types](src/types/qr-code.d.ts) for the complete request/response objects.
 
+#### .getPost(id)
+
+Gets a post.
+
+```javascript
+api.getPost('<user_id>')
+  .then(res => console.log(res.data.aweme_detail))
+  .catch(console.log);
+
+// Outputs:
+// { author: {...}, aweme_id: '999', desc: 'description', music: {...}, statistics: {...}, video: {...}, ... }
+
+```
+
+See the [post types](src/types/post.d.ts) for the complete response object.
+
 #### .listPosts(params)
 
 Lists a user's posts.
diff --git a/src/index.ts b/src/index.ts
index 739c6c5..5004844 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -157,6 +157,19 @@ export default class TikTokAPI {
       },
     )
 
+  /**
+   * Gets a post.
+   *
+   * @param {string} postId
+   * @returns {AxiosPromise<GetPostResponse>}
+   */
+  getPost = (postId: string) =>
+    this.request.get<API.GetPostResponse | API.BaseResponseData>('aweme/v1/aweme/detail/', {
+      params: {
+        aweme_id: postId,
+      },
+    })
+
   /**
    * Lists a user's posts.
    *
diff --git a/src/types/post.d.ts b/src/types/post.d.ts
index 1a20726..b916083 100644
--- a/src/types/post.d.ts
+++ b/src/types/post.d.ts
@@ -1,5 +1,6 @@
 import { CommonUserDetails } from './user';
 import {
+  BaseResponseData,
   CursorOffsetRequestParams,
   CursorOffsetResponseParams,
   ListRequestParams,
@@ -8,6 +9,10 @@ import {
 import { MusicTrack } from './music';
 import { Video } from './video';
 
+export interface GetPostResponse extends BaseResponseData {
+  aweme_detail: Post;
+}
+
 export interface ListPostsRequest extends ListRequestParams, CursorOffsetRequestParams {
   /** The id of the user whose posts to retrieve */
   user_id: string;


Test patch:
diff --git a/test/post.spec.ts b/test/post.spec.ts
index 27a8a50..205d8bf 100644
--- a/test/post.spec.ts
+++ b/test/post.spec.ts
@@ -4,8 +4,10 @@ import { describe, it } from 'mocha';
 
 import TikTokAPI, {
   CommonUserDetails,
+  GetPostResponse,
   ListPostsRequest,
   ListPostsResponse,
+  Post,
 } from '../src';
 import {
   loadTestData,
@@ -13,6 +15,115 @@ import {
   mockParams,
 } from './util';
 
+const expectedPost: Post = {
+  author: {} as CommonUserDetails,
+  author_user_id: '9999999999999999999',
+  aweme_id: '9999999999999999999',
+  aweme_type: 0,
+  create_time: 1546214400,
+  desc: 'example post',
+  music: {
+    author: 'example author',
+    cover_hd: {
+      url_list: [
+        'http://p16.muscdn.com/img/musically-maliva-obj/1000000000000000~c5_2160x2160.jpeg',
+      ],
+    },
+    cover_large: {
+      url_list: [
+        'http://p16.muscdn.com/img/musically-maliva-obj/1000000000000000~c5_1080x1080.jpeg',
+      ],
+    },
+    cover_medium: {
+      url_list: [
+        'http://p16.muscdn.com/img/musically-maliva-obj/1000000000000000~c5_720x720.jpeg',
+      ],
+    },
+    cover_thumb: {
+      url_list: [
+        'http://p16.muscdn.com/img/musically-maliva-obj/1000000000000000~c5_100x100.jpeg',
+      ],
+    },
+    duration: 0,
+    id: '9999999999999999999',
+    owner_handle: 'example handle',
+    owner_id: '9999999999999999999',
+    owner_nickname: 'example nickname',
+    play_url: {
+      url_list: [
+        'http://example.com/track/9999999999999999999.mp3',
+      ],
+    },
+    title: 'example track',
+    user_count: 0,
+  },
+  rate: 10,
+  region: 'US',
+  share_url: 'https://www.musical.ly/v/9999999999999999999.html',
+  statistics: {
+    aweme_id: '9999999999999999999',
+    comment_count: 100,
+    digg_count: 100,
+    play_count: 0,
+    share_count: 100,
+  },
+  status: {
+    allow_comment: true,
+    allow_share: true,
+    is_delete: false,
+    is_private: false,
+  },
+  text_extra: [
+    {
+      hashtag_name: 'example',
+      type: 1,
+    },
+  ],
+  user_digged: 1,
+  video: {
+    cover: {
+      url_list: [
+        'http://p16.muscdn.com/img/musically-maliva-obj/1000000000000000~c5_300x400.jpeg',
+      ],
+    },
+    download_addr: {
+      url_list: [
+        'http://example.com/play/?video_id=9999999999999999999',
+      ],
+    },
+    duration: 15000,
+    has_watermark: true,
+    height: 960,
+    origin_cover: {
+      url_list: [
+        'http://p16.muscdn.com/img/musically-maliva-obj/1000000000000000~c5_900x1200.jpeg',
+      ],
+    },
+    ratio: '720p',
+    width: 540,
+  },
+};
+
+describe('#getPost()', () => {
+  it('a successful response should match the interface', async () => {
+    const api = new TikTokAPI(mockParams, mockConfig);
+    const mock = new MockAdapter(api.request);
+    mock
+      .onGet(new RegExp('aweme/v1/aweme/detail/\?.*'))
+      .reply(200, loadTestData('getPost.json'), {});
+
+    const res = await api.getPost('9999999999999999999');
+    const expected: GetPostResponse = {
+      extra: {
+        now: 1000000000000,
+      },
+      aweme_detail: expectedPost,
+      status_code: 0,
+    };
+    assert.deepStrictEqual(res.data, expected);
+  });
+});
+
 describe('#listPosts()', () => {
   it('a successful response should match the interface', async () => {
     const api = new TikTokAPI(mockParams, mockConfig);
@@ -28,96 +139,7 @@ describe('#listPosts()', () => {
         logid: '20180101000000000000000000000000',
         now: 1000000000000,
       },
-      aweme_list: [
-        {
-          author: {} as CommonUserDetails,
-          author_user_id: '9999999999999999999',
-          aweme_id: '9999999999999999999',
-          aweme_type: 0,
-          create_time: 1546214400,
-          desc: 'example post',
-          music: {
-            author: 'example author',
-            cover_hd: {
-              url_list: [
-                'http://p16.muscdn.com/img/musically-maliva-obj/1000000000000000~c5_2160x2160.jpeg',
-              ],
-            },
-            cover_large: {
-              url_list: [
-                'http://p16.muscdn.com/img/musically-maliva-obj/1000000000000000~c5_1080x1080.jpeg',
-              ],
-            },
-            cover_medium: {
-              url_list: [
-                'http://p16.muscdn.com/img/musically-maliva-obj/1000000000000000~c5_720x720.jpeg',
-              ],
-            },
-            cover_thumb: {
-              url_list: [
-                'http://p16.muscdn.com/img/musically-maliva-obj/1000000000000000~c5_100x100.jpeg',
-              ],
-            },
-            duration: 0,
-            id: '9999999999999999999',
-            owner_handle: 'example handle',
-            owner_id: '9999999999999999999',
-            owner_nickname: 'example nickname',
-            play_url: {
-              url_list: [
-                'http://example.com/track/9999999999999999999.mp3',
-              ],
-            },
-            title: 'example track',
-            user_count: 0,
-          },
-          rate: 10,
-          region: 'US',
-          share_url: 'https://www.musical.ly/v/9999999999999999999.html',
-          statistics: {
-            aweme_id: '9999999999999999999',
-            comment_count: 100,
-            digg_count: 100,
-            play_count: 0,
-            share_count: 100,
-          },
-          status: {
-            allow_comment: true,
-            allow_share: true,
-            is_delete: false,
-            is_private: false,
-          },
-          text_extra: [
-            {
-              hashtag_name: 'example',
-              type: 1,
-            },
-          ],
-          user_digged: 1,
-          video: {
-            cover: {
-              url_list: [
-                'http://p16.muscdn.com/img/musically-maliva-obj/1000000000000000~c5_300x400.jpeg',
-              ],
-            },
-            download_addr: {
-              url_list: [
-                'http://example.com/play/?video_id=9999999999999999999',
-              ],
-            },
-            duration: 15000,
-            has_watermark: true,
-            height: 960,
-            origin_cover: {
-              url_list: [
-                'http://p16.muscdn.com/img/musically-maliva-obj/1000000000000000~c5_900x1200.jpeg',
-              ],
-            },
-            ratio: '720p',
-            width: 540,
-          },
-        },
-      ],
+      aweme_list: [expectedPost],
       has_more: true,
       status_code: 0,
       max_cursor: 1000000000000,
diff --git a/test/testdata/getPost.json b/test/testdata/getPost.json
new file mode 100644
index 0000000..442ae5e
--- /dev/null
+++ b/test/testdata/getPost.json
@@ -0,0 +1,94 @@
+{
+  "extra": {
+    "now": 1000000000000
+  },
+  "aweme_detail": {
+    "author": {},
+    "author_user_id": "9999999999999999999",
+    "aweme_id": "9999999999999999999",
+    "aweme_type": 0,
+    "create_time": 1546214400,
+    "desc": "example post",
+    "music": {
+      "author": "example author",
+      "cover_hd": {
+        "url_list": [
+          "http://p16.muscdn.com/img/musically-maliva-obj/1000000000000000~c5_2160x2160.jpeg"
+        ]
+      },
+      "cover_large": {
+        "url_list": [
+          "http://p16.muscdn.com/img/musically-maliva-obj/1000000000000000~c5_1080x1080.jpeg"
+        ]
+      },
+      "cover_medium": {
+        "url_list": [
+          "http://p16.muscdn.com/img/musically-maliva-obj/1000000000000000~c5_720x720.jpeg"
+        ]
+      },
+      "cover_thumb": {
+        "url_list": [
+          "http://p16.muscdn.com/img/musically-maliva-obj/1000000000000000~c5_100x100.jpeg"
+        ]
+      },
+      "duration": 0,
+      "id": "9999999999999999999",
+      "owner_handle": "example handle",
+      "owner_id": "9999999999999999999",
+      "owner_nickname": "example nickname",
+      "play_url": {
+        "url_list": [
+          "http://example.com/track/9999999999999999999.mp3"
+        ]
+      },
+      "title": "example track",
+      "user_count": 0
+    },
+    "rate": 10,
+    "region": "US",
+    "share_url": "https://www.musical.ly/v/9999999999999999999.html",
+    "statistics": {
+      "aweme_id": "9999999999999999999",
+      "comment_count": 100,
+      "digg_count": 100,
+      "play_count": 0,
+      "share_count": 100
+    },
+    "status": {
+      "allow_comment": true,
+      "allow_share": true,
+      "is_delete": false,
+      "is_private": false
+    },
+    "text_extra": [
+      {
+        "hashtag_name": "example",
+        "type": 1
+      }
+    ],
+    "user_digged": 1,
+    "video": {
+      "cover": {
+        "url_list": [
+          "http://p16.muscdn.com/img/musically-maliva-obj/1000000000000000~c5_300x400.jpeg"
+        ]
+      },
+      "download_addr": {
+        "url_list": [
+          "http://example.com/play/?video_id=9999999999999999999"
+        ]
+      },
+      "duration": 15000,
+      "has_watermark": true,
+      "height": 960,
+      "origin_cover": {
+        "url_list": [
+          "http://p16.muscdn.com/img/musically-maliva-obj/1000000000000000~c5_900x1200.jpeg"
+        ]
+      },
+      "ratio": "720p",
+      "width": 540
+    }
+  },
+  "status_code": 0
+}