Skip to content

feat(zendesk api): Add helper functions to get dynamic data from Zendesk API #101

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 25, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 148 additions & 0 deletions __tests__/services/zendesk-api-service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,154 @@ describe("ZendeskService", () => {
).rejects.toThrow(RangeError);
expect(requestMock).toHaveBeenCalledTimes(0);
});

describe("getTags", () => {
it("should call the API and return the tags", async () => {
const tags = [{ name: "tag1" }];
requestMock.mockResolvedValueOnce({ tags });

const result = await service.getTags();

expect(requestMock).toHaveBeenCalledWith(`/api/v2/tags`);
expect(result).toEqual(tags);
});

it("should continue calling the API until next_page disappears", async () => {
const tags = [{ name: "tag1" }];
requestMock
.mockResolvedValueOnce({ tags, next_page: "next_page" })
.mockResolvedValueOnce({ tags: [] });

const result = await service.getTags();

expect(requestMock).toHaveBeenCalledTimes(2);
expect(requestMock).toHaveBeenNthCalledWith(1, `/api/v2/tags`);
expect(requestMock).toHaveBeenNthCalledWith(2, "next_page");
expect(result).toEqual(tags);
});

it("should only call the API one time with fetchAllTags set to false", async () => {
const tags = [{ name: "tag1" }];
requestMock.mockResolvedValueOnce({ tags, next_page: "next_page" });

const result = await service.getTags(false);

expect(requestMock).toHaveBeenCalledTimes(1);
expect(requestMock).toHaveBeenCalledWith(`/api/v2/tags`);
expect(result).toEqual(tags);
});
});

describe("getGroups", () => {
it("should call the API and return the groups", async () => {
const groups = [{ name: "group1" }];
requestMock.mockResolvedValueOnce({ groups });

const result = await service.getGroups();

expect(requestMock).toHaveBeenCalledWith(`/api/v2/groups`);
expect(result).toEqual(groups);
});

it("should continue calling the API until next_page disappears", async () => {
const groups = [{ name: "group1" }];
requestMock
.mockResolvedValueOnce({ groups, next_page: "next_page" })
.mockResolvedValueOnce({ groups: [] });

const result = await service.getGroups();

expect(requestMock).toHaveBeenCalledTimes(2);
expect(requestMock).toHaveBeenNthCalledWith(1, `/api/v2/groups`);
expect(requestMock).toHaveBeenNthCalledWith(2, "next_page");
expect(result).toEqual(groups);
});

it("should only call the API one time with fetchAllGroups set to false", async () => {
const groups = [{ name: "group1" }];
requestMock.mockResolvedValueOnce({ groups, next_page: "next_page" });

const result = await service.getGroups(false);

expect(requestMock).toHaveBeenCalledTimes(1);
expect(requestMock).toHaveBeenCalledWith(`/api/v2/groups`);
expect(result).toEqual(groups);
});
});

describe("getOrganizations", () => {
it("should call the API and return the organizations", async () => {
const organizations = [{ name: "organization1" }];
requestMock.mockResolvedValueOnce({ organizations });

const result = await service.getOrganizations();

expect(requestMock).toHaveBeenCalledWith(`/api/v2/organizations`);
expect(result).toEqual(organizations);
});

it("should continue calling the API until next_page disappears", async () => {
const organizations = [{ name: "organization1" }];
requestMock
.mockResolvedValueOnce({ organizations, next_page: "next_page" })
.mockResolvedValueOnce({ organizations: [] });

const result = await service.getOrganizations();

expect(requestMock).toHaveBeenCalledTimes(2);
expect(requestMock).toHaveBeenNthCalledWith(1, `/api/v2/organizations`);
expect(requestMock).toHaveBeenNthCalledWith(2, "next_page");
expect(result).toEqual(organizations);
});

it("should only call the API one time with fetchAllOrganizations set to false", async () => {
const organizations = [{ name: "organization1" }];
requestMock.mockResolvedValueOnce({ organizations, next_page: "next_page" });

const result = await service.getOrganizations(false);

expect(requestMock).toHaveBeenCalledTimes(1);
expect(requestMock).toHaveBeenCalledWith(`/api/v2/organizations`);
expect(result).toEqual(organizations);
});
});

describe("getLocales", () => {
it("should call the API and return the locales", async () => {
const locales = [{ locale: "en-US" }];
requestMock.mockResolvedValueOnce({ locales });

const result = await service.getLocales();

expect(requestMock).toHaveBeenCalledWith(`/api/v2/locales`);
expect(result).toEqual(locales);
});

it("should continue calling the API until next_page disappears", async () => {
const locales = [{ locale: "en-US" }];
requestMock
.mockResolvedValueOnce({ locales, next_page: "next_page" })
.mockResolvedValueOnce({ locales: [] });

const result = await service.getLocales();

expect(requestMock).toHaveBeenCalledTimes(2);
expect(requestMock).toHaveBeenNthCalledWith(1, `/api/v2/locales`);
expect(requestMock).toHaveBeenNthCalledWith(2, "next_page");
expect(result).toEqual(locales);
});

it("should only call the API one time with fetchAllLocales set to false", async () => {
const locales = [{ locale: "en-US" }];
requestMock.mockResolvedValueOnce({ locales, next_page: "next_page" });

const result = await service.getLocales(false);

expect(requestMock).toHaveBeenCalledTimes(1);
expect(requestMock).toHaveBeenCalledWith(`/api/v2/locales`);
expect(result).toEqual(locales);
});
});
});
});
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zendesk/zaf-toolbox",
"version": "0.2.9",
"version": "0.2.10",
"description": "A toolbox for ZAF application built with 🩷 by Zendesk Labs",
"main": "lib/src/index.js",
"types": "lib/src/index.d.ts",
Expand Down
70 changes: 70 additions & 0 deletions src/models/zendesk-user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,60 @@ export interface IKeyTitleUserField {
title: string;
}

export interface IZendeskTagsResults extends IZendeskResponse {
tags: IZendeskTag[];
}

export interface IZendeskTag {
count: number;
name: string;
}

export interface IZendeskGroupsResults extends IZendeskResponse {
groups: IZendeskGroup[];
}

export interface IZendeskGroup {
id: number;
name: string;
created_at: string;
updated_at: string;
is_public: boolean;
}

export interface IZendeskOrganizationsResults extends IZendeskResponse {
organizations: IZendeskOrganizations[];
}

export interface IZendeskOrganizations {
id: number;
name: string;
created_at: string;
updated_at: string;
domain_names: string[];
details: string;
notes: string;
group_id: number | null;
shared_tickets: boolean;
shared_comments: boolean;
tags: string[];
external_id: string | null;
url: string;
}

export interface IZendeskLocalesResults extends IZendeskResponse {
locales: IZendeskLocale[];
}

export interface IZendeskLocale {
id: number;
name: string;
locale: string;
created_at: string;
updated_at: string;
url: string;
}

interface IZendeskResponse {
count: number;
next_page: string | null;
Expand All @@ -75,3 +129,19 @@ export interface ISearchUserResults<T = IZendeskUserFieldValue> extends IZendesk
export interface IUserFieldsResults extends IZendeskResponse {
user_fields: IZendeskUserField[];
}

export interface ITagsResults extends IZendeskResponse {
tags: IZendeskTagsResults;
}

export interface IGroupsResults extends IZendeskResponse {
groups: IZendeskGroupsResults;
}

export interface IOrganizationsResults extends IZendeskResponse {
organizations: IZendeskOrganizationsResults;
}

export interface ILocalesResults extends IZendeskResponse {
locales: IZendeskLocalesResults;
}
100 changes: 91 additions & 9 deletions src/services/zendesk-api-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ import {
IUserFieldsResults,
IZendeskUserField,
IZendeskUserFieldValue,
HttpMethod
HttpMethod,
ITagsResults,
IZendeskTagsResults,
IGroupsResults,
IZendeskGroupsResults,
IZendeskOrganizationsResults,
IOrganizationsResults,
IZendeskLocalesResults,
ILocalesResults
} from "@models/index";
import { convertContentMessageToHtml } from "@utils/convert-content-message-to-html";
import { getFromClient } from "@utils/get-from-client";
Expand Down Expand Up @@ -113,10 +121,7 @@ export class ZendeskApiService {
}
}

return results
.flat()
.map(({ users }) => users)
.flat();
return results.map(({ users }) => users).flat();
}

/**
Expand All @@ -137,10 +142,7 @@ export class ZendeskApiService {
}
}

return results
.flat()
.map(({ user_fields }) => user_fields)
.flat();
return results.map(({ user_fields }) => user_fields).flat();
}

/**
Expand Down Expand Up @@ -209,4 +211,84 @@ export class ZendeskApiService {
}
});
}
/**
* Fetch all user instance tags
*/
public async getTags(fetchAllTags = true): Promise<IZendeskTagsResults[]> {
const results = [await this.client.request<string, ITagsResults>(`/api/v2/tags`)];

if (fetchAllTags) {
while (true) {
const nextPage = results[results.length - 1].next_page;

if (!nextPage) {
break;
}

results.push(await this.client.request<string, ITagsResults>(nextPage));
}
}

return results.map(({ tags }) => tags).flat();
}
/**
* Fetch all user instance groups
*/
public async getGroups(fetchAllGroups = true): Promise<IZendeskGroupsResults[]> {
const results = [await this.client.request<string, IGroupsResults>(`/api/v2/groups`)];

if (fetchAllGroups) {
while (true) {
const nextPage = results[results.length - 1].next_page;

if (!nextPage) {
break;
}

results.push(await this.client.request<string, IGroupsResults>(nextPage));
}
}

return results.map(({ groups }) => groups).flat();
}
/**
* Fetch all user instance organizations
*/
public async getOrganizations(fetchAllOrganizations = true): Promise<IZendeskOrganizationsResults[]> {
const results = [await this.client.request<string, IOrganizationsResults>(`/api/v2/organizations`)];

if (fetchAllOrganizations) {
while (true) {
const nextPage = results[results.length - 1].next_page;

if (!nextPage) {
break;
}

results.push(await this.client.request<string, IOrganizationsResults>(nextPage));
}
}

return results.map(({ organizations }) => organizations).flat();
}
/**
* Fetch all user instance locales
*/
public async getLocales(fetchAllLocales = true): Promise<IZendeskLocalesResults[]> {
const results = [await this.client.request<string, ILocalesResults>(`/api/v2/locales`)];

if (fetchAllLocales) {
while (true) {
const nextPage = results[results.length - 1].next_page;

if (!nextPage) {
break;
}

results.push(await this.client.request<string, ILocalesResults>(nextPage));
}
}

return results.map(({ locales }) => locales).flat();
}
}