Skip to content
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

Resource documentation for SDK reference #476

Merged
merged 11 commits into from
Aug 14, 2023
21 changes: 10 additions & 11 deletions src/apiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import fetch, { Request, Response } from 'node-fetch';
import { NylasConfig, OverridableNylasConfig } from './config';
import {
NylasApiError,
NylasAuthError,
NylasTokenValidationError,
NylasOAuthError,
NylasSdkTimeoutError,
} from './models/error';
import { objKeysToCamelCase, objKeysToSnakeCase } from './utils';
import PACKAGE_JSON from '../package.json';

const PACKAGE_JSON = require('../package.json');
const SDK_VERSION = PACKAGE_JSON.version;

export interface RequestOptionsParams {
Expand Down Expand Up @@ -128,7 +128,10 @@ export default class APIClient {
async request<T>(options: RequestOptionsParams): Promise<T> {
const req = this.newRequest(options);
const controller: AbortController = new AbortController();
const timeout = setTimeout(() => controller.abort(), this.timeout);
const timeout = setTimeout(() => {
controller.abort();
throw new NylasSdkTimeoutError(req.url, this.timeout);
}, this.timeout);

const response = await fetch(req, { signal: controller.signal });
clearTimeout(timeout);
Expand All @@ -143,20 +146,16 @@ export default class APIClient {
options.path.includes('connect/token') ||
options.path.includes('connect/revoke');

const tokenErrorResponse = options.path.includes('connect/tokeninfo');

const text = await response.text();
let error: Error;
try {
const parsedError = JSON.parse(text);
const camelCaseError = objKeysToCamelCase(parsedError);

if (authErrorResponse && !tokenErrorResponse) {
error = new NylasAuthError(camelCaseError);
} else if (tokenErrorResponse) {
error = new NylasTokenValidationError(camelCaseError);
if (authErrorResponse) {
error = new NylasOAuthError(camelCaseError, response.status);
} else {
error = new NylasApiError(camelCaseError);
error = new NylasApiError(camelCaseError, response.status);
}
} catch (e) {
throw new Error(
Expand Down
4 changes: 4 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export type NylasConfig = {

export type OverridableNylasConfig = Partial<NylasConfig>;

/**
* An object that can be used to override the default Nylas API client configuration on a per-request basis.
* @property overrides Overrides to the default Nylas API client configuration
*/
export interface Overrides {
overrides?: OverridableNylasConfig;
}
Expand Down
41 changes: 3 additions & 38 deletions src/models/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ type AccessType = 'online' | 'offline';
type Provider = 'google' | 'imap' | 'microsoft' | 'yahoo';

export type URLForAuthenticationConfig = {
clientId: string;
redirectUri: string;
provider?: Provider;
accessType?: AccessType;
Expand All @@ -17,25 +18,6 @@ export type URLForAdminConsentConfig = URLForAuthenticationConfig & {
credentialId: string;
};

export interface OpenID {
iss: string; // Issuer
aud: string; // Application Slug
sub?: string; // ID
email?: string;
emailVerified?: boolean;
atHash?: string;
iat: number; // Issued At
exp: number; // Expites At
// Profile
name?: string;
givenName?: string;
familyName?: string;
nickName?: string;
pictureUrl?: string;
gender?: string;
locale?: string;
}

export interface CodeExchangeRequest {
redirectUri: string;
code: string;
Expand All @@ -45,25 +27,8 @@ export interface CodeExchangeRequest {
export interface TokenExchangeRequest {
redirectUri: string;
refreshToken: string;
}

export type ServerSideHostedAuthRequest = {
redirectUri: string;
provider?: Provider;
state?: string;
loginHint?: string;
cookieNonce?: string;
grantId?: string;
scope?: string[];
expiresIn?: number;
settings?: Record<string, unknown>;
};

export interface ServerSideHostedAuthResponse {
url: string;
id: string;
expiresAt: number;
request: ServerSideHostedAuthRequest;
clientId: string;
clientSecret: string;
}

export interface PKCEAuthURL {
Expand Down
103 changes: 73 additions & 30 deletions src/models/error.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,93 @@
import {
NylasApiErrorResponse,
AuthErrorResponse,
TokenValidationErrorResponse,
} from './response';
import { NylasApiErrorResponse, AuthErrorResponse } from './response';

/**
* Extended Error class for errors returned from the Nylas API
*
* Properties:
* Base class for all Nylas API errors.
*/
export class NylasApiError extends Error {
export abstract class AbstractNylasApiError extends Error {
/**
* The unique identifier of the request.
*/
requestId?: string;
/**
* The HTTP status code of the error response.
*/
statusCode?: number;
}

/**
* Base class for all Nylas SDK errors.
*/
export abstract class AbstractNylasSdkError extends Error {}

/**
* Class representation of a general Nylas API error.
*/
export class NylasApiError extends AbstractNylasApiError {
/**
* Error type.
*/
type: string;
requestId: string;
/**
* Provider Error.
*/
providerError: any;

constructor(apiError: NylasApiErrorResponse) {
constructor(apiError: NylasApiErrorResponse, statusCode?: number) {
super(apiError.error.message);
this.type = apiError.error.type;
this.requestId = apiError.requestId;
this.providerError = apiError.error.providerError;
this.statusCode = statusCode;
}
}

export class NylasAuthError extends Error {
type: string;
requestId: string;
providerError: any;
/**
* Class representing an OAuth error returned by the Nylas API.
*/
export class NylasOAuthError extends AbstractNylasApiError {
/**
* Error type.
*/
error: string;
/**
* Error code used for referencing the docs, logs, and data stream.
*/
errorCode: number;
/**
* Human readable error description.
*/
errorDescription: string;
/**
* URL to the related documentation and troubleshooting regarding this error.
*/
errorUri: string;

constructor(apiError: AuthErrorResponse) {
constructor(apiError: AuthErrorResponse, statusCode?: number) {
super(apiError.errorDescription);
this.type = apiError.error;
this.requestId = apiError.requestId;
this.providerError = apiError.errorDescription;
this.error = apiError.error;
this.errorCode = apiError.errorCode;
this.errorDescription = apiError.errorDescription;
this.errorUri = apiError.errorUri;
this.statusCode = statusCode;
}
}
export class NylasTokenValidationError extends Error {
type: string;
requestId: string;
providerError: any;

constructor(apiError: TokenValidationErrorResponse) {
super(apiError.error.message);
this.type = apiError.error.type;
this.requestId = apiError.error.requestId;
this.providerError = apiError.error.message;
/**
* Error thrown when the Nylas SDK times out before receiving a response from the server
*/
export class NylasSdkTimeoutError extends AbstractNylasSdkError {
/**
* The URL that timed out
*/
url: string;
/**
* The timeout value set in the Nylas SDK, in seconds
*/
timeout: number;

constructor(url: string, timeout: number) {
super('Nylas SDK timed out before receiving a response from the server.');
this.url = url;
this.timeout = timeout;
}
}

// TODO: sdk error models?
15 changes: 4 additions & 11 deletions src/models/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,14 @@ export interface Event {
conferencing: Conferencing;
description?: string;
location?: string;
messageId?: string;
owner?: string;
icalUid?: string;
title?: string;
htmlLink?: string;
hideParticipants?: boolean;
metadata?: Record<string, string>;
creator?: EmailName;
organizer: EmailName;
recurrence?: Recurrence;
recurrence?: string[];
reminders?: Reminder[];
status?: Status;
visibility?: Visibility;
Expand All @@ -41,7 +39,7 @@ export interface CreateEventRequest {
reminderMethod?: string;
metadata?: Record<string, unknown>;
participants?: Participant[];
recurrence?: Recurrence;
recurrence?: string[];
calendarId?: string;
readOnly?: boolean;
roundRobinOrder?: string[];
Expand Down Expand Up @@ -118,8 +116,8 @@ export interface Time {
export interface Timespan {
startTime: number;
endTime: number;
startTimezone: string;
endTimezone: string;
startTimezone?: string;
endTimezone?: string;
}

export interface Date {
Expand All @@ -139,11 +137,6 @@ export interface Participant {
phoneNumber?: string;
}

export interface Recurrence {
rrule: string[];
timezone: string;
}

export interface Reminder {
reminderMinutes: string;
reminderMethod: ReminderMethod;
Expand Down
1 change: 1 addition & 0 deletions src/models/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export interface ProviderDetectParams {

export interface ProviderDetectResponse {
emailAddress: string;
detected: boolean;
provider?: string;
type?: string;
}
Expand Down
12 changes: 0 additions & 12 deletions src/models/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,12 @@ export interface NylasDeleteResponse {
}

export interface AuthErrorResponse {
requestId: string;
error: string;
errorCode: number;
errorDescription: string;
errorUri: string;
}

export interface TokenValidationErrorResponse {
success: boolean;
error: {
httpCode: number;
eventCode: number;
type: string;
message: string;
requestId: string;
};
}

export type ListResponseInnerType<T> = T extends NylasListResponse<infer R>
? R
: never;
9 changes: 2 additions & 7 deletions src/nylas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ import { Calendars } from './resources/calendars';
import { Events } from './resources/events';
import { Auth } from './resources/auth';
import { Webhooks } from './resources/webhooks';
import { Availability } from './resources/availability';
import { Applications } from './resources/applications';

class Nylas {
public applications: Applications;
public availability: Availability;
public auth: Auth;
public calendars: Calendars;
public events: Events;
public webhooks: Webhooks;
Expand All @@ -24,17 +23,13 @@ class Nylas {
});

this.applications = new Applications(this.apiClient);
this.availability = new Availability(this.apiClient);
this.auth = new Auth(this.apiClient);
this.calendars = new Calendars(this.apiClient);
this.events = new Events(this.apiClient);
this.webhooks = new Webhooks(this.apiClient);

return this;
}

public auth(clientId: string, clientSecret: string): Auth {
return new Auth(this.apiClient, clientId, clientSecret);
}
}

export = Nylas;
15 changes: 15 additions & 0 deletions src/resources/applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,29 @@ import { ApplicationDetails } from '../models/applicationDetails';
import { NylasResponse } from '../models/response';
import { Overrides } from '../config';

/**
* Nylas Applications API
*
* This endpoint allows for getting application details as well as redirect URI operations.
*/
export class Applications extends Resource {
/**
* Access the collection of redirect URI related API endpoints.
*/
public redirectUris: RedirectUris;

/**
* @param apiClient client The configured Nylas API client
*/
constructor(apiClient: APIClient) {
super(apiClient);
this.redirectUris = new RedirectUris(apiClient);
}

/**
* Get application details
* @returns The application details
*/
public getDetails({ overrides }: Overrides = {}): Promise<
NylasResponse<ApplicationDetails>
> {
Expand Down
Loading
Loading