diff --git a/index.ts b/index.ts index 8b98937..739a4a7 100644 --- a/index.ts +++ b/index.ts @@ -1,5 +1,4 @@ -import { CreateClient, CreateOptions } from './lib/types'; -import { apiUrls } from './lib/utils/apis'; +import { CreateClient, CreateOptions, apiUrlByResource } from './lib/types'; import { createEvents } from './lib/events'; const _ = require('underscore'); @@ -78,7 +77,7 @@ export const createClient: CreateClient = function (clientOptions) { const requestor = buildRequestor(clientOptions); const options: CreateOptions = { - apiUrls: apiUrls, + apiUrls: apiUrlByResource, requestor: requestor, clientOptions: { accessToken: clientOptions.accessToken || process.env.SMARTSHEET_ACCESS_TOKEN, diff --git a/lib/events/index.ts b/lib/events/index.ts index 47c2151..c578337 100644 --- a/lib/events/index.ts +++ b/lib/events/index.ts @@ -1,30 +1,14 @@ +import { createApiProvider } from '../utils/createApiProvider'; import { EventsApi, GetEventsOptions, GetEventsResponse } from './types'; -import { ClientOptions, CreateOptions, RequestCallback, RequestOptions } from '../types'; - -type OptionsToSend = Partial & { - url: string; -}; +import { ApiResource, CreateOptions, RequestCallback, RequestOptions } from '../types'; export const createEvents = (options: CreateOptions): EventsApi => { - const requester = options.requestor; - - let optionsToSend: OptionsToSend = { - url: options.apiUrls.events, - }; - - if (options.clientOptions) { - optionsToSend = { - ...optionsToSend, - ...options.clientOptions, - }; - } - - return { + return createApiProvider(options, ApiResource.Events, (requester, optionsToSend) => ({ getEvents: ( options: RequestOptions, callback?: RequestCallback ): Promise => { return requester.get({ ...optionsToSend, ...options }, callback); }, - }; + })); }; diff --git a/lib/favorites/endpoints/AddItemsToFavorites.ts b/lib/favorites/endpoints/AddItemsToFavorites.ts new file mode 100644 index 0000000..68b052c --- /dev/null +++ b/lib/favorites/endpoints/AddItemsToFavorites.ts @@ -0,0 +1,60 @@ +import { RequestCallback } from '../../types'; +import { ApiGenerator, FavoritableResource, FavoriteItem, PostResult } from '../sharedTypes'; +const _ = require('underscore'); + +type AddItemsToFavoritesParams = { + body: FavoriteItem | FavoriteItem[]; +}; + +type AddItemsToFavoritesResponse = { + /** + * @description Favorite (object) or Array of Favorite (objects) + */ + result: FavoriteItem | FavoriteItem[]; +} & PostResult; + +type AddItemsToFavoritesRequest = ( + params: AddItemsToFavoritesParams, + callback: RequestCallback +) => Promise; + +export const addItemsToFavorites: ApiGenerator = + (requestor, optionsToSend) => (params, callback) => { + // TODO Bfeigin do i need to explicitly set this to be a body param? + return requestor.post(_.extend({}, optionsToSend, params), callback); + }; + +type AddResourceToFavoritesParams = { + objectId: string; +}; + +type AddItemOfTypeToFavoritesRequest = ( + params: AddResourceToFavoritesParams, + callback: RequestCallback +) => Promise; + +type AddFavoritesBuilder = (resourceType: FavoritableResource) => ApiGenerator; + +export const buildAddFavoriteResourceFn: AddFavoritesBuilder = (resourceType: FavoritableResource) => { + return (requestor, optionsToSend) => (params, callback) => { + const generatedBody = { + objectId: params.objectId, + type: resourceType, + }; + + // Ensure we've extracted out any type and objectId params, placing them into body instead + const constructedPostParams = { + ..._.omit(params, 'type', 'objectId'), + body: generatedBody, + }; + + return requestor.post(_.extend({}, optionsToSend, constructedPostParams), callback); + }; +}; + +export const addSheetToFavorites = buildAddFavoriteResourceFn(FavoritableResource.Sheet); +export const addFolderToFavorites = buildAddFavoriteResourceFn(FavoritableResource.Folder); +export const addReportToFavorites = buildAddFavoriteResourceFn(FavoritableResource.Report); +export const addTemplateToFavorites = buildAddFavoriteResourceFn(FavoritableResource.Template); +export const addWorkspaceToFavorites = buildAddFavoriteResourceFn(FavoritableResource.Workspace); +export const addSightToFavorites = buildAddFavoriteResourceFn(FavoritableResource.Sight); diff --git a/lib/favorites/endpoints/ListFavorites.ts b/lib/favorites/endpoints/ListFavorites.ts new file mode 100644 index 0000000..006c4f6 --- /dev/null +++ b/lib/favorites/endpoints/ListFavorites.ts @@ -0,0 +1,32 @@ +import { RequestCallback } from '../../types'; +import { ApiGenerator, FavoriteItem, Pagination, PaginationResponse } from '../sharedTypes'; + +type IncludeOptions = string; + +type ListFavortesParams = { + /** + * @description If true, include all results, that is, do not paginate. Mutually exclusive with page and pageSize (they are ignored if includeAll=true is specified). + */ + includeAll?: boolean; + /** + * @description A comma-separated list of optional elements to include in the response. + * Enum ["directId", "name"] + */ + include?: IncludeOptions; +} & Pagination; + +export type ListFavoritesResponse = PaginationResponse & { + data: FavoriteItem[]; +}; +/** + * @description Gets a list of all of the user's favorite items. + */ +export type ListFavoritesRequest = ( + params: ListFavortesParams, + callback: RequestCallback +) => Promise; + +export const createListFavorites: ApiGenerator = + (requestor, optionsToSend) => (params, callback) => { + return requestor.get({ ...optionsToSend, ...params }, callback); + }; diff --git a/lib/favorites/endpoints/RemoveFavorites.ts b/lib/favorites/endpoints/RemoveFavorites.ts new file mode 100644 index 0000000..bc103b9 --- /dev/null +++ b/lib/favorites/endpoints/RemoveFavorites.ts @@ -0,0 +1,11 @@ +import { FavoritableResource, FavoriteItem } from '../sharedTypes'; + +type RemoveSingleFavoriteItem = {}; + +type RemoveFavoritesBaseParams = { + /** + * @description A string or array of strings representing the objectIds to remove from favorites + */ + objectIds: FavoriteItem['objectId'] | FavoriteItem['objectId'][]; + favoriteType: FavoritableResource; +}; diff --git a/lib/favorites/index.ts b/lib/favorites/index.ts new file mode 100644 index 0000000..d2f3b4c --- /dev/null +++ b/lib/favorites/index.ts @@ -0,0 +1,60 @@ +import { ApiResource, CreateOptions } from '../types'; +import { createApiProvider } from '../utils/createApiProvider'; +import { createListFavorites } from './endpoints/ListFavorites'; +import { + addFolderToFavorites, + addItemsToFavorites, + addReportToFavorites, + addSheetToFavorites, + addSightToFavorites, + addTemplateToFavorites, + addWorkspaceToFavorites, +} from './endpoints/AddItemsToFavorites'; +import { ApiGenerator } from './sharedTypes'; +import { FavoritesApi } from './types'; + +const buildFavoritesApi: ApiGenerator = (requestor, optionsToSend) => { + return { + listFavorites: createListFavorites(requestor, optionsToSend), + addItemsToFavorites: addItemsToFavorites(requestor, optionsToSend), + addFolderToFavorites: addFolderToFavorites(requestor, optionsToSend), + addReportToFavorites: addReportToFavorites(requestor, optionsToSend), + addSheetToFavorites: addSheetToFavorites(requestor, optionsToSend), + addSightToFavorites: addSightToFavorites(requestor, optionsToSend), + addTemplateToFavorites: addTemplateToFavorites(requestor, optionsToSend), + addWorkspaceToFavorites: addWorkspaceToFavorites(requestor, optionsToSend), + // Duplicate of addItemsToFavorites + addMultipleToFavorites: addItemsToFavorites(requestor, optionsToSend), + }; +}; + +//type FavoritesApi = { +// listFavorites : listFavorites, +// addItemsToFavorites : addItemsToFavorites, +// addSheetToFavorites : addSheetToFavorites, +// addFolderToFavorites : addFolderToFavorites, +// addReportToFavorites : addReportToFavorites, +// addTemplateToFavorites : addTemplateToFavorites, +// addSightToFavorites : addSightToFavorites, +// addWorkspaceToFavorites : addWorkspaceToFavorites, +// addMultipleToFavorites : addMultipleToFavorites, +// removeSheetFromFavorites : removeSheetFromFavorites, +// removeFolderFromFavorites : removeFolderFromFavorites, +// removeReportFromFavorites : removeReportFromFavorites, +// removeTemplateFromFavorites : removeTemplateFromFavorites, +// removeSightFromFavorites : removeSightFromFavorites, +// removeWorkspaceFromFavorites : removeWorkspaceFromFavorites, +// //convenience methods to remove multiples. +// //Uses the same as the singular remove methods. +// removeSheetsFromFavorites : removeSheetFromFavorites, +// removeFoldersFromFavorites : removeFolderFromFavorites, +// removeReportsFromFavorites : removeReportFromFavorites, +// removeTemplatesFromFavorites : removeTemplateFromFavorites, +// removeSightsFromFavorites : removeSightFromFavorites, +// removeWorkspacesFromFavorites : removeWorkspaceFromFavorites +// +//} + +export const createFavorites = (options: CreateOptions) => { + return createApiProvider(options, ApiResource.Favorites, buildFavoritesApi); +}; diff --git a/lib/favorites/sharedTypes.ts b/lib/favorites/sharedTypes.ts new file mode 100644 index 0000000..4133ec6 --- /dev/null +++ b/lib/favorites/sharedTypes.ts @@ -0,0 +1,58 @@ +import { OptionsToSend, Requestor } from '../types'; + +export type Pagination = { + /** + * @description Which page to return. Defaults to 1 if not specified. If you specify a value greater than the total number of pages, the last page of results is returned. + * + */ + page?: number; + /** + * @description The maximum number of items to return per page. Unless otherwise stated for a specific endpoint, defaults to 100. If only page is specified, defaults to a page size of 100. For reports, the default is 100 rows. If you need larger sets of data from your report, returns a maximum of 10,000 rows per request. + */ + pageSize?: number; +}; + +export type PaginationResponse = { + pageNumber: number; + pageSize?: number; + totalPages: number; + totalCount: number; +}; + +export enum FavoritableResource { + Folder = 'folder', + Report = 'report', + Sheet = 'sheet', + Sight = 'sight', + Template = 'template', + Workspace = 'workspace', +} + +export enum ResponseStatusMessage { + PartialSuccess = 'PARTIAL_SUCCESS', + SUCCESS = 'SUCCESS', +} + +export enum ResultCode { + Success = 0, + PartialSuccess = 3, +} + +export type PostResult = { + /** + * @description Message that indicates the outcome of the request. (One of SUCCESS or PARTIAL_SUCCESS) + */ + message: ResponseStatusMessage; + /** + * @description number indicating result status: 0 Success, 3 Partial Success of Bulk Operation + * + */ + resultCode: ResultCode; +}; + +export type FavoriteItem = { + objectId: string; + type: FavoritableResource; +}; + +export type ApiGenerator = (requestor: Requestor, optionsToSend: OptionsToSend) => T; diff --git a/lib/favorites/types.ts b/lib/favorites/types.ts new file mode 100644 index 0000000..d9f26dc --- /dev/null +++ b/lib/favorites/types.ts @@ -0,0 +1,5 @@ +import { ListFavoritesRequest } from "./endpoints/ListFavorites"; + +export type FavoritesApi = { + listFavorites: ListFavoritesRequest; +}; diff --git a/lib/types/ApiUrls.ts b/lib/types/ApiUrls.ts index d349639..d16b664 100644 --- a/lib/types/ApiUrls.ts +++ b/lib/types/ApiUrls.ts @@ -1,20 +1,47 @@ -export interface ApiUrls { - contacts: string; - events: string; - favorites: string; - folders: string; - groups: string; - home: string; - imageUrls: string; - reports: string; - search: string; - server: string; - sheets: string; - sights: string; - templates: string; - templatesPublic: string; - token: string; - users: string; - webhooks: string; - workspaces: string; +export enum ApiResource { + Contacts = 'contacts', + Events = 'events', + Favorites = 'favorites', + Folders = 'folders', + Groups = 'groups', + Home = 'home', + ImageUrls = 'imageurls', + Reports = 'reports', + Search = 'search', + Server = 'serverinfo', + Sheets = 'sheets', + Sights = 'sights', + Templates = 'templates', + TemplatesPublic = 'templatespublic', + Token = 'token', + Users = 'users', + Webhooks = 'webhooks', + Workspaces = 'workspaces', } + +export const apiUrlByResource = { + [ApiResource.Contacts]: 'contacts/', + [ApiResource.Events]: 'events/', + [ApiResource.Favorites]: 'favorites/', + [ApiResource.Folders]: 'folders/', + [ApiResource.Groups]: 'groups/', + [ApiResource.Home]: 'home/', + [ApiResource.ImageUrls]: 'imageurls/', + [ApiResource.Reports]: 'reports/', + [ApiResource.Search]: 'search/', + [ApiResource.Server]: 'serverinfo/', + [ApiResource.Sheets]: 'sheets/', + [ApiResource.Sights]: 'sights/', + [ApiResource.Templates]: 'templates/', + [ApiResource.TemplatesPublic]: 'templates/public', + [ApiResource.Token]: 'token', + [ApiResource.Users]: 'users/', + [ApiResource.Webhooks]: 'webhooks/', + [ApiResource.Workspaces]: 'workspaces/', +} as const; + +// Map of ApiResource to path +export type ApiUrlPathByResource = typeof apiUrlByResource; + +// Valid paths for Api Resources +export type ApiUrlPath = ApiUrlPathByResource[ApiResource]; diff --git a/lib/types/CreateOptions.ts b/lib/types/CreateOptions.ts index 2b048f7..aac048b 100644 --- a/lib/types/CreateOptions.ts +++ b/lib/types/CreateOptions.ts @@ -1,10 +1,30 @@ -import { ApiUrls } from './ApiUrls'; +import { ApiUrlPathByResource } from './ApiUrls'; import { CreateClientOptions } from './CreateClientOptions'; export type ClientOptions = Pick; +export interface Requestor { + get: (options: any, callback: any) => any; + put: (options: any, callback: any) => any; + post: (options: any, callback: any) => any; + postFile: (options: any, callback: any) => any; + delete: (options: any, callback: any) => any; + internal: { + buildHeaders: (options: any) => { + Accept: any; + 'Content-Type': any; + 'User-Agent': string; + }; + buildUrl: (options: any) => any; + }; +} + export interface CreateOptions { - apiUrls: ApiUrls; - requestor: any; + apiUrls: ApiUrlPathByResource; + requestor: Requestor; clientOptions?: ClientOptions; } + +export type OptionsToSend = Partial & { + url: string; +}; diff --git a/lib/utils/apis.ts b/lib/utils/apis.ts index 9ed792a..5fe19ba 100644 --- a/lib/utils/apis.ts +++ b/lib/utils/apis.ts @@ -1,21 +1,6 @@ -import { ApiUrls } from '../types'; -export const apiUrls: ApiUrls = { - contacts: 'contacts/', - events: 'events/', - favorites: 'favorites/', - folders: 'folders/', - groups: 'groups/', - home: 'home/', - imageUrls: 'imageurls/', - reports: 'reports/', - search: 'search/', - server: 'serverinfo/', - sheets: 'sheets/', - sights: 'sights/', - templates: 'templates/', - templatesPublic: 'templates/public', - token: 'token', - users: 'users/', - webhooks: 'webhooks/', - workspaces: 'workspaces/', -}; +import { apiUrlByResource } from '../types'; + +/* + * @depricated - Will be removed in v5, prefer apiUrlByResource instead + */ +export { apiUrlByResource as apiUrls }; diff --git a/lib/utils/createApiProvider.ts b/lib/utils/createApiProvider.ts new file mode 100644 index 0000000..b02a611 --- /dev/null +++ b/lib/utils/createApiProvider.ts @@ -0,0 +1,22 @@ +import { ApiResource, CreateOptions, OptionsToSend, Requestor } from '../types'; + +export const createApiProvider = ( + options: CreateOptions, + ApiResource: ApiResource, + createApi: (requestor: Requestor, optionsToSend: OptionsToSend) => ApiContract +) => { + const requestor: Requestor = options.requestor; + + let optionsToSend: OptionsToSend = { + url: options.apiUrls[ApiResource], + }; + + if (options.clientOptions) { + optionsToSend = { + ...optionsToSend, + ...options.clientOptions, + }; + } + + return createApi(requestor, optionsToSend); +};