Skip to content

Commit

Permalink
Merge pull request #30 from leoferreiralima/feat/get-content-detail
Browse files Browse the repository at this point in the history
feat: add content detail api
  • Loading branch information
leoferreiralima committed Sep 27, 2023
2 parents 852760c + 8adbf8f commit 11ebbcd
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 18 deletions.
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,30 @@ const response = await tabNews.contents.create({
status: 'published',
});
```

**Buscar Detalhe do Conteúdo**

```js
import { TabNews } from 'tabnews-sdk';

const tabNews = new TabNews();

const content = await tabNews.contents.getBySlug({
slug: '<slug>',
username: '<username>',
});
```

```js
import { TabNews } from 'tabnews-sdk';

const tabNews = new TabNews();

await tabNews.session.create();

// Não é preciso passar o username pois internamente a bliblioteca ira realizar o fecth do usuario atual

const content = await tabNews.contents.getBySlug({
slug: '<slug>',
});
```
56 changes: 49 additions & 7 deletions src/content/__snapshots__/content.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ exports[`Content > create > should create content 1`] = `

exports[`Content > create > should throw an error when create content with invalid parameters 1`] = `"\\"body\\" possui o valor inválido \\"null\\"."`;

exports[`Content > get > should return all contents and pagination 1`] = `
exports[`Content > get > should get all contents and pagination 1`] = `
{
"first_page": 1,
"last_page": 6,
Expand All @@ -34,7 +34,7 @@ exports[`Content > get > should return all contents and pagination 1`] = `
}
`;

exports[`Content > get > should return all contents and pagination 2`] = `
exports[`Content > get > should get all contents and pagination 2`] = `
[
{
"children_deep_count": 2,
Expand All @@ -55,7 +55,7 @@ exports[`Content > get > should return all contents and pagination 2`] = `
]
`;

exports[`Content > get > should return all contents for a specific user 1`] = `
exports[`Content > get > should get all contents for a specific user 1`] = `
{
"first_page": 1,
"last_page": 6,
Expand All @@ -68,7 +68,7 @@ exports[`Content > get > should return all contents for a specific user 1`] = `
}
`;

exports[`Content > get > should return all contents for a specific user 2`] = `
exports[`Content > get > should get all contents for a specific user 2`] = `
[
{
"children_deep_count": 2,
Expand All @@ -89,7 +89,7 @@ exports[`Content > get > should return all contents for a specific user 2`] = `
]
`;

exports[`Content > get > should return all contents for current user 1`] = `
exports[`Content > get > should get all contents for current user 1`] = `
{
"first_page": 1,
"last_page": 6,
Expand All @@ -102,7 +102,7 @@ exports[`Content > get > should return all contents for current user 1`] = `
}
`;

exports[`Content > get > should return all contents for current user 2`] = `
exports[`Content > get > should get all contents for current user 2`] = `
[
{
"children_deep_count": 2,
Expand All @@ -123,6 +123,46 @@ exports[`Content > get > should return all contents for current user 2`] = `
]
`;

exports[`Content > get > should get content by slug 1`] = `
{
"body": "body",
"children_deep_count": 2,
"created_at": 2023-09-19T12:16:04.812Z,
"deleted_at": null,
"id": "id",
"owner_id": "owner_id",
"owner_username": "username",
"parent_id": null,
"published_at": 2023-09-19T12:16:04.837Z,
"slug": "slug",
"source_url": "https://source.url.com/source",
"status": "published",
"tabcoins": 1,
"title": "title",
"updated_at": 2023-09-19T12:16:04.812Z,
}
`;

exports[`Content > get > should get content by slug for current user 1`] = `
{
"body": "body",
"children_deep_count": 2,
"created_at": 2023-09-19T12:16:04.812Z,
"deleted_at": null,
"id": "id",
"owner_id": "owner_id",
"owner_username": "username",
"parent_id": null,
"published_at": 2023-09-19T12:16:04.837Z,
"slug": "slug",
"source_url": "https://source.url.com/source",
"status": "published",
"tabcoins": 1,
"title": "title",
"updated_at": 2023-09-19T12:16:04.812Z,
}
`;

exports[`Content > get > should return correct page when is last page 1`] = `
{
"first_page": 1,
Expand Down Expand Up @@ -157,4 +197,6 @@ exports[`Content > get > should return correct page when is last page 2`] = `
]
`;

exports[`Content > get > should return error when parameter is invalid 1`] = `"\\"page\\" deve possuir um valor mínimo de 1."`;
exports[`Content > get > should throw an error when content not found 1`] = `"O conteúdo informado não foi encontrado no sistema."`;

exports[`Content > get > should throw an error when parameter is invalid 1`] = `"\\"page\\" deve possuir um valor mínimo de 1."`;
87 changes: 79 additions & 8 deletions src/content/content.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
resetMocks,
} from '@test/utils';

import { GetContentParams } from './interfaces';
import { GetContentListParams } from './interfaces';

let tabNews: TabNews;

Expand All @@ -29,6 +29,8 @@ const lastPageLinkHeader =
'<https://www.tabnews.com.br/api/v1/contents?strategy=relevant&page=5&per_page=30>; rel="prev", ' +
'<https://www.tabnews.com.br/api/v1/contents?strategy=relevant&page=6&per_page=30>; rel="last"';

const username = DEFAULT_USER.username;

const content = {
id: 'id',
owner_id: 'owner_id',
Expand All @@ -42,10 +44,15 @@ const content = {
published_at: '2023-09-19T12:16:04.837Z',
deleted_at: null,
tabcoins: 1,
owner_username: 'username',
owner_username: username,
children_deep_count: 2,
};

const contentDetail = {
...content,
body: 'body',
};

describe('Content', () => {
beforeEach(() => {
tabNews = createTabNews();
Expand All @@ -71,7 +78,14 @@ describe('Content', () => {
);
};

it('should return all contents and pagination', async () => {
const mockContent = (slug: string, user: string = username) => {
mockOnceResponse(
`${TABNEWS_ENDPOINTS.content}/${user}/${slug}`,
contentDetail,
);
};

it('should get all contents and pagination', async () => {
mockContents(linkHeader);

const response = await tabNews.content.getAll();
Expand Down Expand Up @@ -99,7 +113,7 @@ describe('Content', () => {
});

it('should send all content params correctly', async () => {
const paramsList: GetContentParams[] = [
const paramsList: GetContentListParams[] = [
{
page: 2,
per_page: 10,
Expand Down Expand Up @@ -134,7 +148,7 @@ describe('Content', () => {
expectRequest(third).query('strategy').toBe('old');
});

it('should return all contents for a specific user', async () => {
it('should get all contents for a specific user', async () => {
const username = 'username';
mockContents(linkHeader, username);

Expand All @@ -150,10 +164,10 @@ describe('Content', () => {
expectRequest(request).method.toBeGet();
});

it('should return all contents for current user', async () => {
it('should get all contents for current user', async () => {
mockOnceCurrentUser();

mockContents(linkHeader, DEFAULT_USER.username);
mockContents(linkHeader, username);

const response = await tabNews.content.getMy();

Expand All @@ -165,7 +179,7 @@ describe('Content', () => {
expectRequest(request).method.toBeGet();
});

it('should return error when parameter is invalid', () => {
it('should throw an error when parameter is invalid', () => {
mockOnceApiError(TABNEWS_ENDPOINTS.content, {
name: 'ValidationError',
message: '"page" deve possuir um valor mínimo de 1.',
Expand All @@ -184,6 +198,63 @@ describe('Content', () => {
}),
).rejects.toThrowErrorMatchingSnapshot();
});

it('should get content by slug', async () => {
const slug = 'slug';

mockContent(slug);

const content = await tabNews.content.getBySlug({
slug,
username,
});

expect(content).toMatchSnapshot();

const request = mockedRequest();

expectRequest(request).method.toBeGet();
});

it('should get content by slug for current user', async () => {
const slug = 'slug';

mockOnceCurrentUser();

mockContent(slug);

const content = await tabNews.content.getBySlug({
slug,
});

expect(content).toMatchSnapshot();

const request = mockedRequest();

expectRequest(request).method.toBeGet();
});

it('should throw an error when content not found', () => {
const slug = 'slug';

mockOnceApiError(`${TABNEWS_ENDPOINTS.content}/${username}/${slug}`, {
name: 'NotFoundError',
message: 'O conteúdo informado não foi encontrado no sistema.',
action: 'Verifique se o "slug" está digitado corretamente.',
status_code: 404,
error_id: '3ea15e67-97c8-4671-916f-0344934c8300',
request_id: '11815650-d56e-4b90-97dd-dcdf23df8412',
error_location_code: 'CONTROLLER:CONTENT:GET_HANDLER:SLUG_NOT_FOUND',
key: 'slug',
});

expect(() =>
tabNews.content.getBySlug({
slug,
username,
}),
).rejects.toThrowErrorMatchingSnapshot();
});
});

describe('create', () => {
Expand Down
23 changes: 21 additions & 2 deletions src/content/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { TabNews } from '@/tabnews';
import { parseLink } from '@/utils';

import {
ContentDetailResponse,
ContentPagination,
ContentResponse,
ContentStrategy,
CreateContent,
CreateContentResponse,
GetContentListParams,
GetContentParams,
} from './interfaces';

Expand All @@ -23,7 +25,7 @@ export class Content {
return content;
}

async getAll({ username, ...params }: GetContentParams = {}) {
async getAll({ username, ...params }: GetContentListParams = {}) {
const urlParams = new URLSearchParams();

Object.entries(params).forEach(([key, value]) => {
Expand Down Expand Up @@ -62,9 +64,26 @@ export class Content {
};
}

async getMy(params: Omit<GetContentParams, 'username'> = {}) {
async getMy(params: Omit<GetContentListParams, 'username'> = {}) {
const { username } = await this.tabNews.user.me();

return await this.getAll({ username, ...params });
}

async getBySlug(params: GetContentParams) {
const { body: content } = await this.tabNews.get<ContentDetailResponse>({
path: await this.getUrlForSlugAndUsername(params),
});

return content;
}

private async getUrlForSlugAndUsername({ slug, username }: GetContentParams) {
if (username) {
return `${TABNEWS_ENDPOINTS.content}/${username}/${slug}`;
}
const { username: currentUser } = await this.tabNews.user.me();

return `${TABNEWS_ENDPOINTS.content}/${currentUser}/${slug}`;
}
}
11 changes: 10 additions & 1 deletion src/content/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export interface ContentResponse {
children_deep_count: number;
}

export interface GetContentParams {
export interface GetContentListParams {
username?: string;
page?: number;
per_page?: number;
Expand Down Expand Up @@ -62,3 +62,12 @@ export interface CreateContentResponse {
deleted_at?: Date;
tabcoins: number;
}

export interface GetContentParams {
slug: string;
username?: string;
}

export interface ContentDetailResponse extends ContentResponse {
body: string;
}
2 changes: 2 additions & 0 deletions test/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ const parseCookie = (cookie: string | null) =>
);

export function expectRequest(request: Request) {
expect(request, 'request must be called').toBeDefined();

const url = new URL(request.url);

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down

0 comments on commit 11ebbcd

Please sign in to comment.