partial implementation of v3 api

This commit is contained in:
2024-12-21 08:18:26 +00:00
parent f77bed5979
commit 1c36cdb7d5
69 changed files with 1387 additions and 257 deletions
+71
View File
@@ -0,0 +1,71 @@
import D4HRequest from '../d4hRequest'
import { EntityType } from '../entity'
import type { Animal } from '../types/animal'
import { GetAnimalsOptions, D4H_BASE_URL } from '../d4h'
export class animalsApi {
static async getAnimal(request: D4HRequest, context: string, contextId: number, animalId: number | 'me'): Promise<Animal> {
const url = new URL(`${D4H_BASE_URL}/${context}/${contextId}/animals/${animalId}`)
const animal = await request.getAsync<Animal>(url)
if (!animal) {
throw new Error('Animal data not found or improperly formatted.')
}
animal.entityType = EntityType.Animal
return animal
}
static async getAnimals(request: D4HRequest, context: string, contextId: number, options?: GetAnimalsOptions): Promise<Animal[]> {
const url = new URL(`${D4H_BASE_URL}/${context}/${contextId}/animals`)
if (options !== undefined) {
const optionsList = url.searchParams
if (options.handler_member_id !== undefined) {
optionsList.append('handler_member_id', options.handler_member_id.toString())
}
if (options.id !== undefined) {
optionsList.append('id', options.id.toString())
}
if (options.order !== undefined) {
optionsList.append('order', options.order)
}
if (options.page !== undefined) {
optionsList.append('page', options.page.toString())
}
if (options.size !== undefined) {
optionsList.append('size', options.size.toString())
}
if (options.sort !== undefined) {
if (Array.isArray(options.sort)) {
options.sort.forEach(sortField => optionsList.append('sort', sortField))
} else {
optionsList.append('sort', options.sort)
}
}
if (options.status !== undefined) {
optionsList.append('status', options.status)
}
if (options.team_id !== undefined) {
optionsList.append('team_id', options.team_id.toString())
}
}
const animals = await request.getManyAsync<Animal>(url)
animals.forEach(m => m.entityType = EntityType.Animal)
return animals
}
}
+74
View File
@@ -0,0 +1,74 @@
import D4HRequest from '../d4hRequest'
import { EntityType } from '../entity'
import type { memberGroup } from '../types/group'
import { GetMemberGroupsOptions, D4H_BASE_URL } from '../d4h'
export class groupsApi {
static async getMemberGroup(
request: D4HRequest,
context: string,
contextId: number,
groupId: number,
): Promise<memberGroup> {
const url = new URL(`${D4H_BASE_URL}/${context}/${contextId}/member-groups/${groupId}`)
const group = await request.getAsync<memberGroup>(url)
if (!group) {
throw new Error('Group data not found or improperly formatted.')
}
group.entityType = EntityType.memberGroup
return group
}
static async getMemberGroups(
request: D4HRequest,
context: string,
contextId: number,
options?: GetMemberGroupsOptions
): Promise<memberGroup[]> {
const url = new URL(`${D4H_BASE_URL}/${context}/${contextId}/member-groups`)
if (options !== undefined) {
const optionsList = url.searchParams
if (options.id !== undefined) {
optionsList.append('id_tag', options.id.toString())
}
if (options.order !== undefined) {
optionsList.append('order', options.order)
}
if (options.page !== undefined) {
optionsList.append('page', options.page.toString())
}
if (options.size !== undefined) {
optionsList.append('size', options.size.toString())
}
if (options.sort !== undefined) {
if (Array.isArray(options.sort)) {
options.sort.forEach((sortField) => optionsList.append('sort', sortField))
} else {
optionsList.append('sort', options.sort)
}
}
if (options.team_id !== undefined) {
optionsList.append('team_id', options.team_id.toString())
}
if (options.title !== undefined) {
optionsList.append('title', options.title)
}
}
const groups = await request.getManyAsync<memberGroup>(url)
groups.forEach((m) => (m.entityType = EntityType.memberGroup))
return groups
}
}
+97
View File
@@ -0,0 +1,97 @@
import D4HRequest from '../d4hRequest'
import { EntityType } from '../entity'
import type { Member } from '../types/member'
import { GetMemberOptions, GetMembersOptions, D4H_BASE_URL } from '../d4h'
export class membersApi {
static async getMember(
request: D4HRequest,
context: string,
contextId: number,
id: number | 'me',
options?: GetMemberOptions
): Promise<Member> {
const url = new URL(`${D4H_BASE_URL}/${context}/${contextId}/members/${id}`)
if (options !== undefined) {
const optionsList = url.searchParams
if (options.includeDetails !== undefined) {
optionsList.append('include_details', 'true')
}
}
const member = await request.getAsync<Member>(url)
if (!member) {
throw new Error('Member data not found or improperly formatted.')
}
member.entityType = EntityType.Member
return member
}
static async getMembers(
request: D4HRequest,
context: string,
contextId: number,
options?: GetMembersOptions
): Promise<Member[]> {
const url = new URL(`${D4H_BASE_URL}/${context}/${contextId}/members`)
if (options !== undefined) {
const optionsList = url.searchParams
if (options.deleted !== undefined) {
optionsList.append('deleted', options.deleted.toString())
}
if (options.id_tag !== undefined) {
optionsList.append('id_tag', options.id_tag)
}
if (options.name !== undefined) {
optionsList.append('name', options.name)
}
if (options.order !== undefined) {
optionsList.append('order', options.order)
}
if (options.page !== undefined) {
optionsList.append('page', options.page.toString())
}
if (options.size !== undefined) {
optionsList.append('size', options.size.toString())
}
if (options.sort !== undefined) {
if (Array.isArray(options.sort)) {
options.sort.forEach((sortField) => optionsList.append('sort', sortField))
} else {
optionsList.append('sort', options.sort)
}
}
if (options.statuses !== undefined) {
options.statuses.forEach((status) => {
if (status !== null) {
optionsList.append('statuses', status.toString())
} else {
optionsList.append('statuses', 'null')
}
})
}
if (options.team_id !== undefined) {
optionsList.append('team_id', options.team_id.toString())
}
}
const members = await request.getManyAsync<Member>(url)
members.forEach((m) => (m.entityType = EntityType.Member))
return members
}
}
+213
View File
@@ -0,0 +1,213 @@
import D4HRequest from '../d4hRequest'
import { EntityType } from '../entity'
import type { Qualification, MemberAwards } from '../types/qualification'
import { D4H_BASE_URL, GetQualificationOptions, GetMemberAwardsOptions } from '../d4h'
export class qualificationsApi {
static async getMemberQualification(request: D4HRequest, context: string, contextId: number, qualificationId: number | 'me'): Promise<Qualification> {
const url = new URL(`${D4H_BASE_URL}/${context}/${contextId}/member-qualifications/${qualificationId}`)
const qualification = await request.getAsync<Qualification>(url)
if (!qualification) {
throw new Error('Qualification data not found or improperly formatted.')
}
qualification.entityType = EntityType.Incident
return qualification
}
static async getMemberQualifications(request: D4HRequest, context: string, contextId: number, options?: GetQualificationOptions): Promise<Qualification[]> {
const url = new URL(`${D4H_BASE_URL}/${context}/${contextId}/member-qualifications`)
if (options !== undefined) {
const optionsList = url.searchParams
if (options.exclude_org_data !== undefined) {
optionsList.append('exclude_org_data', options.exclude_org_data.toString())
}
if (options.exclude_teams_data !== undefined) {
optionsList.append('exclude_teams_data', options.exclude_teams_data.toString())
}
if (options.order !== undefined) {
optionsList.append('order', options.order)
}
if (options.page !== undefined) {
optionsList.append('page', options.page.toString())
}
if (options.size !== undefined) {
optionsList.append('size', options.size.toString())
}
if (options.sort !== undefined) {
if (Array.isArray(options.sort)) {
options.sort.forEach(sortField => optionsList.append('sort', sortField))
} else {
optionsList.append('sort', options.sort)
}
}
if (options.title !== undefined) {
optionsList.append('title', options.title)
}
}
return request.getManyAsync(url)
}
static async getAnimalQualification(request: D4HRequest, context: string, contextId: number, qualificationId: number): Promise<Qualification> {
const url = new URL(`${D4H_BASE_URL}/${context}/${contextId}/animal-qualifications/${qualificationId}`)
const qualification = await request.getAsync<Qualification>(url)
if (!qualification) {
throw new Error('Qualification data not found or improperly formatted.')
}
qualification.entityType = EntityType.Qualification
return qualification
}
static async getAnimalQualifications(request: D4HRequest, context: string, contextId: number, options?: GetQualificationOptions): Promise<Qualification[]> {
const url = new URL(`${D4H_BASE_URL}/${context}/${contextId}/animal-qualifications`)
if (options !== undefined) {
const optionsList = url.searchParams
if (options.exclude_org_data !== undefined) {
optionsList.append('exclude_org_data', options.exclude_org_data.toString())
}
if (options.exclude_teams_data !== undefined) {
optionsList.append('exclude_teams_data', options.exclude_teams_data.toString())
}
if (options.order !== undefined) {
optionsList.append('order', options.order)
}
if (options.page !== undefined) {
optionsList.append('page', options.page.toString())
}
if (options.size !== undefined) {
optionsList.append('size', options.size.toString())
}
if (options.sort !== undefined) {
if (Array.isArray(options.sort)) {
options.sort.forEach(sortField => optionsList.append('sort', sortField))
} else {
optionsList.append('sort', options.sort)
}
}
if (options.title !== undefined) {
optionsList.append('title', options.title)
}
}
return request.getManyAsync(url)
}
static async getHandlerQualification(request: D4HRequest, context: string, contextId: number, qualificationId: number): Promise<Qualification> {
const url = new URL(`${D4H_BASE_URL}/${context}/${contextId}/handler-qualifications/${qualificationId}`)
const qualification = await request.getAsync<Qualification>(url)
if (!qualification) {
throw new Error('Qualification data not found or improperly formatted.')
}
qualification.entityType = EntityType.Qualification
return qualification
}
static async getHandlerQualifications(request: D4HRequest, context: string, contextId: number, options?: GetQualificationOptions): Promise<Qualification[]> {
const url = new URL(`${D4H_BASE_URL}/${context}/${contextId}/handler-qualifications`)
if (options !== undefined) {
const optionsList = url.searchParams
if (options.exclude_org_data !== undefined) {
optionsList.append('exclude_org_data', options.exclude_org_data.toString())
}
if (options.exclude_teams_data !== undefined) {
optionsList.append('exclude_teams_data', options.exclude_teams_data.toString())
}
if (options.order !== undefined) {
optionsList.append('order', options.order)
}
if (options.page !== undefined) {
optionsList.append('page', options.page.toString())
}
if (options.size !== undefined) {
optionsList.append('size', options.size.toString())
}
if (options.sort !== undefined) {
if (Array.isArray(options.sort)) {
options.sort.forEach(sortField => optionsList.append('sort', sortField))
} else {
optionsList.append('sort', options.sort)
}
}
if (options.title !== undefined) {
optionsList.append('title', options.title)
}
}
return request.getManyAsync(url)
}
static async getMemberAwards(request: D4HRequest, context: string, contextId: number, options?: GetMemberAwardsOptions): Promise<MemberAwards[]> {
const url = new URL(`${D4H_BASE_URL}/${context}/${contextId}/member-qualification-awards`)
if (options !== undefined) {
const optionsList = url.searchParams
if (options.exclude_org_data !== undefined) {
optionsList.append('exclude_org_data', options.exclude_org_data.toString())
}
if (options.exclude_teams_data !== undefined) {
optionsList.append('exclude_teams_data', options.exclude_teams_data.toString())
}
if (options.member_id !== undefined) {
optionsList.append('member_id', options.member_id.toString())
}
if (options.order !== undefined) {
optionsList.append('order', options.order)
}
if (options.page !== undefined) {
optionsList.append('page', options.page.toString())
}
if (options.qualification_id !== undefined) {
optionsList.append('qualification_id', options.qualification_id.toString())
}
if (options.size !== undefined) {
optionsList.append('size', options.size.toString())
}
if (options.sort !== undefined) {
if (Array.isArray(options.sort)) {
options.sort.forEach(sortField => optionsList.append('sort', sortField))
} else {
optionsList.append('sort', options.sort)
}
}
}
const awards = await request.getManyAsync<MemberAwards>(url)
awards.forEach((m) => (m.entityType = EntityType.Award))
return awards
}
}
+262 -69
View File
@@ -1,21 +1,46 @@
import { CustomFieldUpdate } from './customField'
import { CustomFieldUpdate } from './types/customField'
import D4HRequest from './d4hRequest'
import { EmergencyContacts } from './emergencyContacts'
import { Entity, EntityType } from './entity'
import type { Group } from './group'
import type { Member, MemberUpdate } from './member'
import type { memberGroup } from './types/group'
import type { Member, MemberUpdate } from './types/member'
import type { Qualification, MemberAwards } from './types/qualification'
import type { Incident } from './types/incident'
import type { Animal } from './types/animal'
import { membersApi } from './api/membersApi'
import { animalsApi } from './api/animalsApi'
import { qualificationsApi } from './api/qualificationsApi'
import { groupsApi } from './api/groupsApi'
const D4H_FETCH_LIMIT = 250
const D4H_BASE_URL = 'https://api.d4h.org/v2'
const D4H_BASE_URL = 'https://api.team-manager.us.d4h.com/v3' // Multiple API endpoints now, probably should handle multiple
export { D4H_BASE_URL }
export interface GetMemberOptions {
includeDetails?: boolean;
}
export interface GetMembersOptions {
groupId?: number;
includeCustomFields?: boolean;
includeDetails?: boolean;
deleted?: boolean; // default: false
id_tag?: string;
name?: string;
order?: 'asc' | 'desc'; // default: 'asc'
page?: number;
size?: number;
sort?: string | string[]; // default: 'id'
statuses?: (number | null)[]; // list of ids or null
team_id?: number; // the numeric identifier for a team resource
}
export interface GetAnimalsOptions {
handler_member_id?: number | number[];
id?: number | number[];
order?: 'asc' | 'desc'; // default: 'asc'
page?: number;
size?: number;
sort?: string | string[]; // default: 'id'
status?: string; // list of ids or null
team_id?: number; // the numeric identifier for a team resource
}
export interface GetGroupsOptions {
@@ -23,6 +48,55 @@ export interface GetGroupsOptions {
title?: string;
}
export interface GetIncidentOptions {
after?: string;
before?: string;
deleted?: boolean; // default: 'false'
ends_before?: string;
id?: number | number[];
order?: 'asc' | 'desc'; // default: 'asc'
page?: number;
published?: boolean;
reference?: string;
size?: number;
sort?: string | string[]; // default: 'id'
starts_after?: string;
tag_bundle_id?: number;
tag_id?: number;
team_id?: number;
}
export interface GetQualificationOptions {
exclude_org_data?: boolean; // default: false
exclude_teams_data?: boolean; // default: false
order?: 'asc' | 'desc'; // default: 'asc'
page?: number;
size?: number;
sort?: string | string[]; // default: 'id'
title?: string;
}
export interface GetMemberAwardsOptions {
exclude_org_data?: boolean; // default: false
exclude_teams_data?: boolean; // default: false
member_id?: number | 'me';
order?: 'asc' | 'desc'; // default: 'asc'
page?: number;
qualification_id?: number;
size?: number;
sort?: string | string[]; // default: 'id'
}
export interface GetMemberGroupsOptions {
id: number;
order?: 'asc' | 'desc'; // default: 'asc'
page?: number;
size?: number;
sort?: string | string[]; // default: 'id'
team_id?: number | number[];
title?: string;
}
export default class D4H {
private readonly _request: D4HRequest
@@ -34,110 +108,229 @@ export default class D4H {
/**************** MEMBERS *******************/
/********************************************/
async getMemberAsync(id: number, options?: GetMemberOptions): Promise<Member> {
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')
}
}
const member = await this._request.getAsync<Member>(url)
member.type = EntityType.Member
return member
async getMember(
context: string,
contextId: number,
id: number | 'me',
options?: GetMemberOptions
): Promise<Member> {
return membersApi.getMember(this._request, context, contextId, id, options)
}
async getMembersAsync(options?: GetMembersOptions): Promise<Member[]> {
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')
}
}
const members = await this._request.getManyAsync<Member>(url)
members.forEach(m => m.type = EntityType.Member)
return members
async getMembers(
context: string,
contextId: number,
options?: GetMembersOptions
): Promise<Member[]> {
return membersApi.getMembers(this._request, context, contextId, options)
}
updateMemberAsync(id: number, updates: MemberUpdate): Promise<void> {
updateMember(context: string, contextId: number, id: number, updates: MemberUpdate): Promise<void> {
// If no updates, no need to actually make a request. Exit early.
if (Object.getOwnPropertyNames(updates).length === 0) {
return Promise.resolve()
}
const url = new URL(`${D4H_BASE_URL}/team/members/${id}`)
const url = new URL(`${D4H_BASE_URL}/${context}/${contextId}/members/${id}`)
return this._request.putAsync(url, updates)
}
/********************************************/
/**************** ANIMALS *******************/
/********************************************/
async getAnimal(
context: string,
contextId: number,
id: number | 'me',
): Promise<Animal> {
return animalsApi.getAnimal(this._request, context, contextId, id)
}
async getAnimals(
context: string,
contextId: number,
options?: GetAnimalsOptions
): Promise<Animal[]> {
return animalsApi.getAnimals(this._request, context, contextId, options)
}
/********************************************/
/*********** EMERGENCY CONTACTS *************/
/********************************************/
getEmergencyContacts(memberId: number): Promise<EmergencyContacts> {
const url = new URL(`${D4H_BASE_URL}/team/members/${memberId}/emergency`)
return this._request.getAsync(url)
}
updateEmergencyContacts(memberId: number, emergencyContacts: EmergencyContacts): Promise<EmergencyContacts> {
const url = new URL(`${D4H_BASE_URL}/team/members/${memberId}/emergency`)
return this._request.putAsync(url, emergencyContacts)
}
// Emergency Contacts no longer have specific endpoint
/********************************************/
/***************** GROUPS *******************/
/********************************************/
getGroupAsync(id: number): Promise<Group> {
const url = new URL(`${D4H_BASE_URL}/team/groups/${id}`)
return this._request.getAsync<Group>(url)
async getMemberGroup(
context: string,
contextId: number,
groupId: number,
): Promise<memberGroup> {
return groupsApi.getMemberGroup(this._request, context, contextId, groupId)
}
getGroupsAsync(options?: GetGroupsOptions): Promise<Group[]> {
const url = new URL(`${D4H_BASE_URL}/team/groups`)
async getMemberGroups(
context: string,
contextId: number,
options?: GetMemberGroupsOptions
): Promise<memberGroup[]> {
return groupsApi.getMemberGroups(this._request, context, contextId, options)
}
/********************************************/
/**************** INCIDENTS *****************/
/********************************************/
async getIncident(context: string, contextId: number, activityId: number): Promise<Incident> {
const url = new URL(`${D4H_BASE_URL}/${context}/${contextId}/incidents/${activityId}`)
const incident = await this._request.getAsync<Incident>(url)
if (!incident) {
throw new Error('Incident data not found or improperly formatted.')
}
incident.entityType = EntityType.Incident
return incident
}
async getIncidents(context: string, contextId: number, options?: GetIncidentOptions): Promise<Incident[]> {
const url = new URL(`${D4H_BASE_URL}/${context}/${contextId}/incidents`)
if (options !== undefined) {
const optionsList = url.searchParams
if (options.memberId !== undefined) {
optionsList.append('member_id', options.memberId.toString())
if (options.after !== undefined) {
optionsList.append('after', options.after)
}
if (options.title !== undefined) {
optionsList.append('title', options.title)
if (options.before !== undefined) {
optionsList.append('before', options.before)
}
if (options.deleted !== undefined) {
optionsList.append('order', options.deleted.toString())
}
if (options.ends_before !== undefined) {
optionsList.append('ends_before', options.ends_before)
}
if (options.id !== undefined) {
optionsList.append('id', options.id.toString())
}
if (options.order !== undefined) {
optionsList.append('order', options.order)
}
if (options.page !== undefined) {
optionsList.append('page', options.page.toString())
}
if (options.published !== undefined) {
optionsList.append('published', options.published.toString())
}
if (options.reference !== undefined) {
optionsList.append('reference', options.reference)
}
if (options.size !== undefined) {
optionsList.append('size', options.size.toString())
}
if (options.sort !== undefined) {
if (Array.isArray(options.sort)) {
options.sort.forEach(sortField => optionsList.append('sort', sortField))
} else {
optionsList.append('sort', options.sort)
}
}
if (options.starts_after !== undefined) {
optionsList.append('starts_after', options.starts_after)
}
if (options.tag_bundle_id !== undefined) {
optionsList.append('tag_bundle_id', options.tag_bundle_id.toString())
}
if (options.tag_id !== undefined) {
optionsList.append('tag_id', options.tag_id.toString())
}
if (options.team_id !== undefined) {
optionsList.append('team_id', options.team_id.toString())
}
}
return this._request.getManyAsync(url)
}
/********************************************/
/************* QUALIFICATIONS ***************/
/********************************************/
async getMemberQualification(
context: string,
contextId: number,
id: number | 'me',
): Promise<Qualification> {
return qualificationsApi.getMemberQualification(this._request, context, contextId, id)
}
async getMemberQualifications(
context: string,
contextId: number,
options?: GetQualificationOptions
): Promise<Qualification[]> {
return qualificationsApi.getMemberQualifications(this._request, context, contextId, options)
}
async getAnimalQualification(
context: string,
contextId: number,
id: number,
): Promise<Qualification> {
return qualificationsApi.getAnimalQualification(this._request, context, contextId, id)
}
async getAnimalQualifications(
context: string,
contextId: number,
options?: GetQualificationOptions
): Promise<Qualification[]> {
return qualificationsApi.getAnimalQualifications(this._request, context, contextId, options)
}
async getHandlerQualification(
context: string,
contextId: number,
id: number,
): Promise<Qualification> {
return qualificationsApi.getHandlerQualification(this._request, context, contextId, id)
}
async getHandlerQualifications(
context: string,
contextId: number,
options?: GetQualificationOptions
): Promise<Qualification[]> {
return qualificationsApi.getHandlerQualifications(this._request, context, contextId, options)
}
async getMemberAwards(
context: string,
contextId: number,
options?: GetMemberAwardsOptions
): Promise<MemberAwards[]> {
return qualificationsApi.getMemberAwards(this._request, context, contextId, options)
}
/********************************************/
/************** CUSTOM FIELDS ***************/
/********************************************/
updateCustomFields(entity: Entity, updates: CustomFieldUpdate[], onlyMemberEditOwn: boolean): Promise<void> {
// If no updates, no need to actually make a request. Exit early.
if (updates.length === 0) {
return Promise.resolve()
}
const url = new URL(`${D4H_BASE_URL}/team/custom-fields/${entity.type}/${entity.id}`)
const url = new URL(`${D4H_BASE_URL}/team/custom-fields/${entity.entityType}/${entity.id}`)
// From the documentation:
// https://api.d4h.org/v2/documentation#operation/putTeamCustomfieldsEntity_typeEntity_id
@@ -187,4 +380,4 @@ export default class D4H {
return this._request.putAsync(url, { fields: updates })
}
}
}
+30 -24
View File
@@ -1,12 +1,12 @@
interface D4HResponse<DataType> {
statusCode: number
data: DataType
status: number
data?: DataType
}
interface D4HError {
error: string
message: string
statusCode: number
statusText: string
status: number
}
enum HttpMethod {
@@ -46,39 +46,45 @@ export default class D4HRequest {
const rawResponse = await fetch(url.toString(), options)
const response = await rawResponse.json() as D4HResponse<TResponse> & D4HError
if (response.statusCode !== 200) {
const d4hError = response as D4HError
throw new Error(`${d4hError.statusCode}: ${d4hError.error}: ${d4hError.message}`)
if (rawResponse.status !== 200) { // error handling may need to be updated
console.log(rawResponse)
throw new Error(`${rawResponse.status} : ${rawResponse.statusText}`)
}
return response.data
return response as TResponse
}
async getAsync<DataType>(url: URL): Promise<DataType> {
return this.requestAsync<never, DataType>(url, HttpMethod.Get)
}
async getManyAsync<DataType>(url: URL): Promise<DataType[]> {
async getManyAsync<DataType>(url: URL, paginate: boolean = false): Promise<DataType[]> {
let results: DataType[] = []
let offset = 0
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, no-constant-condition
while (true) {
const urlWithOffset = new URL(url)
urlWithOffset.searchParams.append('offset', offset.toString())
urlWithOffset.searchParams.append('limit', this._fetchLimit.toString())
const newResults = await this.getAsync<DataType[]>(urlWithOffset)
results = results.concat(newResults)
offset += this._fetchLimit
if (newResults.length < this._fetchLimit) {
break
if (paginate) {
let offset = 0
// Pagination loop
while (true) {
const urlWithOffset = new URL(url)
urlWithOffset.searchParams.append('offset', offset.toString())
urlWithOffset.searchParams.append('limit', this._fetchLimit.toString())
const newResults = await this.getAsync<DataType[]>(urlWithOffset)
results = results.concat(newResults)
offset += this._fetchLimit
if (newResults.length < this._fetchLimit) {
break
}
}
} else {
// Fetch without pagination
const newResults = await this.getAsync<DataType[]>(url)
results = results.concat(newResults)
}
return results
}
+7 -2
View File
@@ -1,9 +1,9 @@
import { CustomField } from './customField'
import { CustomField } from './types/customField'
export interface Entity {
custom_fields?: CustomField[];
id: number;
type: EntityType;
entityType: EntityType;
}
// EntityType must be one of:
@@ -13,4 +13,9 @@ export interface Entity {
// Only the ones actively in use are implemented.
export enum EntityType {
Member = 'member',
memberGroup = 'membergroup',
Incident = 'incident',
Qualification = 'qualification',
Award = 'award',
Animal = 'animal'
}
-5
View File
@@ -1,5 +0,0 @@
export interface Group {
bundle: string
id: number
title: string
}
-62
View File
@@ -1,62 +0,0 @@
import { EmergencyContact } from './emergencyContacts'
import { Entity } from './entity'
export interface MemberStatus {
id: number
type: string
value: string
label: MemberStatusLabel | null
}
export interface MemberStatusLabel {
id: number
value: string
}
export interface Member extends Entity {
address: string
email: string | null
emergency_contacts: EmergencyContact[]
group_ids: number[] | null
homephone: string
joined_at: string
mobilephone: string
name: string
notes: string | null
position: string
ref: string
status: MemberStatus
workphone: string
}
export interface MemberUpdate {
name?: string | null
ref?: string | null
id_tag?: string | null
status_id?: number
status_custom_id?: number
retired_reason_id?: number
date_leave?: Date
date_join?: Date
position?: string | null
role_id?: number
cost_per_hour?: number
cost_per_use?: number
address_street?: string | null
address_city?: string | null
address_region?: string | null
address_postcode?: string | null
address_country?: string | null
lat?: number
lng?: number
gridref?: string | null
location_bookmark_id?: number
email?: string | null
phone_mobile?: string | null
phone_home?: string | null
phone_work?: string | null
pager?: string | null
pager_email?: string | null
address?: string | null
notes?: string | null
}
+18
View File
@@ -0,0 +1,18 @@
import { Entity } from '../entity'
export interface Animal extends Entity {
id: number;
name: string;
breed: string;
type: string;
countRollingHours: number;
ref: string;
notes: string;
status: string;
resourceType: string;
bornAt: string;
joinedAt: string;
leftAt: string | null;
createdAt: string;
updatedAt: string;
}
@@ -1,14 +1,3 @@
export interface Incident {
id: number,
ref_desc: string,
date: string,
enddate?: string,
description?: string,
lat?: number,
lng?: number,
tags?: string[],
}
export interface IncidentRoster {
id: number,
status: string,
+11
View File
@@ -0,0 +1,11 @@
import { Entity } from '../entity'
export interface memberGroup extends Entity{
id: number;
title: string;
deprecatedBundle: string;
membershipResourceType: string;
resourceType: string;
createdAt: string;
updatedAt: string;
}
+72
View File
@@ -0,0 +1,72 @@
import { Entity } from '../entity'
export interface IncidentOwner {
resourceType: string;
id: number;
}
export interface IncidentLocation {
type: string;
coordinates: [number, number];
}
export interface WeatherInfo {
symbol: string | null;
symbolDate: string | null;
temperature: number | null;
}
export interface AddressInfo {
postCode: string;
region: string;
street: string;
town: string;
country: string;
}
export interface IncidentLocationBookmark {
id: number | null;
resourceType: string;
}
export interface IncidentTag {
id: number;
resourceType: string;
}
export interface Incident extends Entity{
id: number;
owner: IncidentOwner;
resourceType: string;
reference: string;
referenceDescription: string;
bearing: number;
coordinator: string | null;
countAttendance: number;
countGuests: number;
createdAt: string;
createdOrPublishedAt: string;
description: string;
descriptionDeprecated: string;
distance: number;
night: boolean;
percAttendance: number;
plan: string | null;
planDeprecated: string | null;
published: boolean;
approved: boolean;
shared: boolean;
trackingNumber: string | null;
updatedAt: string;
startsAt: string;
endsAt: string;
weather: WeatherInfo;
weatherPressure: number | null;
weatherCloudCover: number | null;
address: AddressInfo;
location: IncidentLocation;
locationBookmark: IncidentLocationBookmark;
fullTeam: boolean;
selfCoordinator: boolean;
tags: IncidentTag[];
}
+156
View File
@@ -0,0 +1,156 @@
import { Entity } from '../entity'
export interface MemberStatus {
id: number
type: string
value: string
label: MemberStatusLabel | null
}
export interface MemberStatusLabel {
id: number
value: string
}
export interface CustomMemberStatus {
id: number | null
resourceType: string
}
export interface EquipmentLocation {
id: number | null
resourceType: string
}
export interface EmailInfo {
value: string
verified: boolean
}
export interface PhoneInfo {
phone: string
verified?: boolean
}
export interface PrimaryEmergencyContact {
name: string
primaryPhone: string
secondaryPhone: string
relation: string
}
export interface SecondaryEmergencyContact {
name: string
primaryPhone: string
secondaryPhone: string
relation: string
}
export interface RetiredReason {
id: number | null
resourceType: string
}
export interface Role {
id: number | null
resourceType: string
}
export interface MemberLocationBookmark {
id: number | null
resourceType: string
}
export interface Location {
type: string
coordinates: [number, number]
}
export interface Member extends Entity {
id: number
name: string
ref: string
status: string
position: string
createdAt: string
updatedAt: string
startsAt: string | null
endsAt: string | null
lastLogin: string | null
weeklyDayOfWeek: number
weeklyDayOfWeekUtc: number
weeklyHourOfDay: number
weeklyHourOfDayUtc: number
email: EmailInfo
home: PhoneInfo
mobile: PhoneInfo
work: PhoneInfo
pager: PhoneInfo
notes: string | null
permission: number
credits: number
defaultDuty: string
defaultEquipmentLocation: EquipmentLocation
customStatus: CustomMemberStatus
location: Location
locationBookmark: MemberLocationBookmark
retiredReason: RetiredReason
role: Role
primaryEmergencyContact: PrimaryEmergencyContact
secondaryEmergencyContact: SecondaryEmergencyContact
alertActivityApproval: boolean
alertAllQualifications: boolean
alertGear: boolean
alertQualifications: boolean
chatAutosubscribe: boolean
chatDailyDigest: boolean
contactUpdateMail: boolean
weeklyMail: boolean
deprecatedAddress: string | null
icalSecret: string | null
costPerHour: number | null
costPerUse: number | null
countReportingEvent: number
countReportingExercise: number
countReportingHours: number
countReportingIncident: number
countRollingHours: number
countRollingHoursEvent: number
countRollingHoursExercise: number
countRollingHoursIncident: number
percReportingEvent: number
percReportingExercise: number
percReportingIncident: number
percRollingEvent: number
percRollingExercise: number
percRollingIncident: number
signedTandC: string | null
teamAgreementSigned: string | null
}
export interface MemberUpdate {
name?: string | null
ref?: string | null
id_tag?: string | null
status_id?: number
status_custom_id?: number
retired_reason_id?: number
date_leave?: Date
date_join?: Date
position?: string | null
role_id?: number
cost_per_hour?: number
cost_per_use?: number
lat?: number
lng?: number
gridref?: string | null
location_bookmark_id?: number
email?: string | null
phone_mobile?: string | null
phone_home?: string | null
phone_work?: string | null
pager?: string | null
pager_email?: string | null
address?: string | null
notes?: string | null
}
+32
View File
@@ -0,0 +1,32 @@
import { Entity } from '../entity'
export interface Resource {
resourceType: string;
id: number;
}
export interface Qualification extends Entity {
id: number,
cost: number | null,
createdAt: string,
description: string,
expiredCost: null,
reminderDays: number,
title: string,
updatedAt: string,
resourceType: string,
deprecatedBundle: string,
expiresMonthsDefault: number | null,
}
export interface MemberAwards extends Entity {
id: number;
createdAt: string;
updatedAt: string;
resourceType: string;
startsAt: string | null;
endsAt: string | null;
owner: Resource;
member: Resource;
qualification: Resource;
}