From ae7d6f98ac09c477e14e093cdd275a27ec5e5cc9 Mon Sep 17 00:00:00 2001 From: Colin Williams <126836598+colincwilliams-kcsara@users.noreply.github.com> Date: Fri, 17 Mar 2023 14:15:19 -0700 Subject: [PATCH] Add linter support * Added linter and fixed errors * Updated error handling to only throw error objects --------- Co-authored-by: Colin Williams --- lib/.eslintrc.json | 46 +++++++++++ lib/index.ts | 12 +-- lib/package.json | 11 ++- lib/src/d4h.ts | 190 +++++++++++++++++++++---------------------- lib/src/group.ts | 6 +- lib/src/httpUtils.ts | 69 ++++++++-------- lib/src/member.ts | 96 +++++++++++----------- 7 files changed, 243 insertions(+), 187 deletions(-) create mode 100644 lib/.eslintrc.json diff --git a/lib/.eslintrc.json b/lib/.eslintrc.json new file mode 100644 index 0000000..d12826c --- /dev/null +++ b/lib/.eslintrc.json @@ -0,0 +1,46 @@ +{ + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking", + "plugin:@typescript-eslint/strict" + ], + "overrides": [ + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "project": "./tsconfig.json" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "indent": [ + "error", + 4 + ], + "linebreak-style": [ + "error", + "windows" + ], + "quotes": [ + "error", + "single" + ], + "semi": [ + "error", + "never" + ], + "no-unexpected-multiline": [ + "error" + ], + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": ["error"], + "@typescript-eslint/prefer-namespace-keyword": "off" + } +} diff --git a/lib/index.ts b/lib/index.ts index 3c02c59..6b88022 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,6 +1,6 @@ -import D4H from './src/d4h.js'; -export default D4H; - -export * from './src/d4h.js' -export * from './src/group.js' -export * from './src/member.js' +import D4H from './src/d4h.js' +export default D4H + +export * from './src/d4h.js' +export * from './src/group.js' +export * from './src/member.js' diff --git a/lib/package.json b/lib/package.json index 7b556b4..dcfc74a 100644 --- a/lib/package.json +++ b/lib/package.json @@ -4,10 +4,17 @@ "main": "./built/index.js", "license": "MIT", "scripts": { - "build": "npx tsc" + "launch": "npm run build && node .", + "build": "npm run lint && npx tsc", + "build-submodules": "cd ./submodules/d4h-typescript/lib && npm run build", + "clean": "npx tsc --build --clean", + "lint": "eslint --ext .ts,.d.ts ./src" }, "devDependencies": { - "typescript": "^4.8.4" + "typescript": "^4.8.4", + "@typescript-eslint/eslint-plugin": "^5.55.0", + "@typescript-eslint/parser": "^5.55.0", + "eslint": "^8.36.0" }, "dependencies": { } diff --git a/lib/src/d4h.ts b/lib/src/d4h.ts index 3f06afc..f878d6d 100644 --- a/lib/src/d4h.ts +++ b/lib/src/d4h.ts @@ -1,96 +1,96 @@ -import HttpUtils from "./httpUtils"; -import type { Group } from "./group"; -import type { Member } from "./member"; - -const D4H_FETCH_LIMIT = 250; -const D4H_BASE_URL = "https://api.d4h.org/v2"; - -export interface GetMemberOptions { - includeDetails?: boolean; -} - -export interface GetMembersOptions { - groupId?: number; - includeCustomFields?: boolean; - includeDetails?: boolean; -} - -export interface GetGroupsOptions { - memberId?: number; - title?: string; -} - -export default class D4H { - private readonly _httpUtils: HttpUtils; - - constructor(token: string) { - this._httpUtils = new HttpUtils(token, D4H_FETCH_LIMIT); - } - - /********************************************/ - /**************** MEMBERS *******************/ - /********************************************/ - - async getMember(id: number, options?: GetMemberOptions): Promise { - let url = new URL(`${D4H_BASE_URL}/team/members/${id}`) - - if (options !== undefined) { - let optionsList = url.searchParams; - - if (options.includeDetails !== undefined) { - optionsList.append("include_details", "true"); - } - } - - return await this._httpUtils.get(url); - } - - async getMembers(options?: GetMembersOptions): Promise { - let url = new URL(`${D4H_BASE_URL}/team/members`) - - if (options !== undefined) { - let optionsList = url.searchParams; - - if (options.groupId !== undefined) { - optionsList.append("group_id", options.groupId.toString()); - } - - if (options.includeDetails !== undefined) { - optionsList.append("include_details", "true"); - } - - if (options.includeCustomFields !== undefined) { - optionsList.append("include_custom_fields", "true"); - } - } - - return await this._httpUtils.getMany(url); - } - - /********************************************/ - /***************** GROUPS *******************/ - /********************************************/ - - async getGroup(id: number): Promise { - let url = new URL(`${D4H_BASE_URL}/team/groups/${id}`) - return await this._httpUtils.get(url); - } - - async getGroups(options?: GetGroupsOptions): Promise { - let url = new URL(`${D4H_BASE_URL}/team/groups`) - - if (options !== undefined) { - let optionsList = url.searchParams; - - if (options.memberId !== undefined) { - optionsList.append("member_id", options.memberId.toString()); - } - - if (options.title !== undefined) { - optionsList.append("title", options.title); - } - } - - return await this._httpUtils.getMany(url); - } +import HttpUtils from './httpUtils' +import type { Group } from './group' +import type { Member } from './member' + +const D4H_FETCH_LIMIT = 250 +const D4H_BASE_URL = 'https://api.d4h.org/v2' + +export interface GetMemberOptions { + includeDetails?: boolean; +} + +export interface GetMembersOptions { + groupId?: number; + includeCustomFields?: boolean; + includeDetails?: boolean; +} + +export interface GetGroupsOptions { + memberId?: number; + title?: string; +} + +export default class D4H { + private readonly _httpUtils: HttpUtils + + constructor(token: string) { + this._httpUtils = new HttpUtils(token, D4H_FETCH_LIMIT) + } + + /********************************************/ + /**************** MEMBERS *******************/ + /********************************************/ + + async getMember(id: number, options?: GetMemberOptions): Promise { + const url = new URL(`${D4H_BASE_URL}/team/members/${id}`) + + if (options !== undefined) { + const optionsList = url.searchParams + + if (options.includeDetails !== undefined) { + optionsList.append('include_details', 'true') + } + } + + return await this._httpUtils.get(url) + } + + async getMembers(options?: GetMembersOptions): Promise { + const url = new URL(`${D4H_BASE_URL}/team/members`) + + if (options !== undefined) { + const optionsList = url.searchParams + + if (options.groupId !== undefined) { + optionsList.append('group_id', options.groupId.toString()) + } + + if (options.includeDetails !== undefined) { + optionsList.append('include_details', 'true') + } + + if (options.includeCustomFields !== undefined) { + optionsList.append('include_custom_fields', 'true') + } + } + + return await this._httpUtils.getMany(url) + } + + /********************************************/ + /***************** GROUPS *******************/ + /********************************************/ + + async getGroup(id: number): Promise { + const url = new URL(`${D4H_BASE_URL}/team/groups/${id}`) + return await this._httpUtils.get(url) + } + + async getGroups(options?: GetGroupsOptions): Promise { + const url = new URL(`${D4H_BASE_URL}/team/groups`) + + if (options !== undefined) { + const optionsList = url.searchParams + + if (options.memberId !== undefined) { + optionsList.append('member_id', options.memberId.toString()) + } + + if (options.title !== undefined) { + optionsList.append('title', options.title) + } + } + + return await this._httpUtils.getMany(url) + } } \ No newline at end of file diff --git a/lib/src/group.ts b/lib/src/group.ts index ca8871c..8b87f58 100644 --- a/lib/src/group.ts +++ b/lib/src/group.ts @@ -1,5 +1,5 @@ export interface Group { - bundle: string; - id: number; - title: string; + bundle: string + id: number + title: string } \ No newline at end of file diff --git a/lib/src/httpUtils.ts b/lib/src/httpUtils.ts index e33ef31..eac3c82 100644 --- a/lib/src/httpUtils.ts +++ b/lib/src/httpUtils.ts @@ -1,63 +1,66 @@ interface D4HResponse { - statusCode: number; - data: DataType; - error: string; + statusCode: number + data: DataType } interface D4HError { - statusCode: number; - error: string; + error: string + message: string + statusCode: number } export default class HttpUtils { - private readonly _fetchLimit: number; - private readonly _token: string; + private readonly _fetchLimit: number + private readonly _token: string constructor(token: string, fetchLimit: number) { if (!token) { - throw new Error("Token cannot be empty"); + throw new Error('Token cannot be empty') } - this._fetchLimit = fetchLimit; - this._token = token; + this._fetchLimit = fetchLimit + this._token = token } async get(url: URL): Promise { - let method = "GET"; - let headers = { - "Authorization": `Bearer ${this._token}`, - }; - - console.log(url); - - let rawResponse = await fetch(url.toString(), { method, headers }); - let response = await rawResponse.json() as D4HResponse; - - if (response.statusCode !== 200) { - throw response as D4HError; + const method = 'GET' + const headers = { + 'Authorization': `Bearer ${this._token}`, } - return response.data as DataType; + console.log(url) + + const rawResponse = await fetch(url.toString(), { method, headers }) + const response = await rawResponse.json() as D4HResponse & D4HError + + if (response.statusCode !== 200) { + const d4hError = response as D4HError + throw new Error(`${d4hError.statusCode}: ${d4hError.error}: ${d4hError.message}`) + } + + return response.data } async getMany(url: URL): Promise { - let results: DataType[] = []; + let results: DataType[] = [] - let offset = 0; + let offset = 0 + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, no-constant-condition while (true) { - let urlWithOffset = new URL(url); - urlWithOffset.searchParams.append('offset', offset.toString()); - urlWithOffset.searchParams.append('limit', this._fetchLimit.toString()); + const urlWithOffset = new URL(url) + urlWithOffset.searchParams.append('offset', offset.toString()) + urlWithOffset.searchParams.append('limit', this._fetchLimit.toString()) - let newResults = await this.get(urlWithOffset); - results = results.concat(newResults); - offset += this._fetchLimit; + const newResults = await this.get(urlWithOffset) + results = results.concat(newResults) + offset += this._fetchLimit if (newResults.length < this._fetchLimit) { - break; + break } } - return results; + return results } } diff --git a/lib/src/member.ts b/lib/src/member.ts index fb620f3..2eb1b7b 100644 --- a/lib/src/member.ts +++ b/lib/src/member.ts @@ -1,49 +1,49 @@ -export enum CustomFieldType { - Number = "number", - Text = "text", - Date = "date", -} - -export interface CustomField { - id: number; - type: CustomFieldType; - label: string; - value_string: string | null; - value: string | null; -} - -export interface EmergencyContact { - name: string | null; - relation: string | null; - phone: string | null; - alt_phone: string | null; -} - -export interface MemberStatus { - id: number; - type: string; - value: string; - label: MemberStatusLabel | null; -} - -export interface MemberStatusLabel { - id: number; - value: string; -} - -export interface Member { - address: string; - custom_fields?: CustomField[]; - email: string | null; - emergency_contacts: EmergencyContact[]; - group_ids: number[] | null; - homephone: string; - id: number; - mobilephone: string; - name: string; - notes: string | null; - position: string; - ref: string; - status: MemberStatus; - workphone: string; +export enum CustomFieldType { + Number = 'number', + Text = 'text', + Date = 'date', +} + +export interface CustomField { + id: number; + type: CustomFieldType; + label: string; + value_string: string | null; + value: string | null; +} + +export interface EmergencyContact { + name: string | null; + relation: string | null; + phone: string | null; + alt_phone: string | null; +} + +export interface MemberStatus { + id: number; + type: string; + value: string; + label: MemberStatusLabel | null; +} + +export interface MemberStatusLabel { + id: number; + value: string; +} + +export interface Member { + address: string; + custom_fields?: CustomField[]; + email: string | null; + emergency_contacts: EmergencyContact[]; + group_ids: number[] | null; + homephone: string; + id: number; + mobilephone: string; + name: string; + notes: string | null; + position: string; + ref: string; + status: MemberStatus; + workphone: string; } \ No newline at end of file