mirror of
https://github.com/kc4x4sar/d4h-typescript.git
synced 2026-06-03 09:23:36 -07:00
partial implementation of v3 api
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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'
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
export interface Group {
|
||||
bundle: string
|
||||
id: number
|
||||
title: string
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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[];
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user