@managemint-solutions/sdk 0.52.0 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -10
- package/dist/audit/dto.d.ts +3 -8
- package/dist/audit/dto.js +7 -48
- package/dist/audit/index.d.ts +61 -6
- package/dist/audit/index.js +218 -47
- package/dist/clients/index.d.ts +3 -1
- package/dist/clients/index.js +3 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -3
- package/dist/leads/index.d.ts +3 -2
- package/dist/leads/index.js +3 -2
- package/dist/leave/leave-adjustments.js +1 -0
- package/dist/leave/leave-entitlements.d.ts +3 -1
- package/dist/leave/leave-entitlements.js +3 -0
- package/dist/leave/leave-requests.d.ts +3 -1
- package/dist/leave/leave-requests.js +3 -0
- package/dist/organization-payment-methods/index.d.ts +5 -6
- package/dist/organization-payment-methods/index.js +4 -17
- package/dist/organizations/index.d.ts +3 -1
- package/dist/organizations/index.js +29 -1
- package/dist/permissions/index.js +2 -0
- package/dist/projects/index.d.ts +3 -1
- package/dist/projects/index.js +3 -0
- package/dist/service-client/index.d.ts +7 -0
- package/dist/service-client/index.js +35 -1
- package/dist/tasks/index.d.ts +11 -8
- package/dist/tasks/index.js +16 -12
- package/dist/timesheets/timesheet-entries.js +1 -0
- package/dist/timesheets/timesheets.d.ts +3 -1
- package/dist/timesheets/timesheets.js +3 -0
- package/dist/users/index.d.ts +3 -1
- package/dist/users/index.js +3 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
// their classes are not exported: a consumer never constructs one, and every export is a contract
|
|
8
8
|
// to keep. The row and input types they produce and accept are exported for annotations.
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.
|
|
11
|
-
exports.
|
|
12
|
-
exports.monthlyTotalMinor = exports.weekStartOf = exports.minutesBetween = exports.UpdateTimesheetEntryDto = exports.TimesheetIdDto = exports.TimesheetEntryIdDto = exports.ReviewTimesheetDto = exports.GetTimesheetsDto = exports.GetTimesheetReportDto = exports.CreateTimesheetEntryDto = exports.todayInJohannesburg = exports.shiftDays = exports.proRataDays = void 0;
|
|
10
|
+
exports.PermissionsResource = exports.UpdateOrganizationDto = exports.CreateOrganizationDto = exports.NotificationIdDto = exports.GetNotificationsDto = exports.NoteIdDto = exports.GetNotesDto = exports.AddNoteDto = exports.UpdateAdditionalDetailsDto = exports.IsAdditionalDetailsValueByType = exports.GetAdditionalDetailsDto = exports.AdditionalDetailsIdDto = exports.AddAdditionalDetailsDto = exports.UpdateLabelDto = exports.LabelIdDto = exports.GetLabelsDto = exports.AddLabelDto = exports.UpdateStatusDto = exports.StatusIdDto = exports.ReorderStatusesDto = exports.GetStatusesDto = exports.AddStatusDto = exports.CreateSupportRequestDto = exports.UpdateConfigDto = exports.CreateConfigDto = exports.CreateFeatureRequestDto = exports.InvoiceIdDto = exports.GetInvoicesQueryDto = exports.ReinstateSubscriptionDto = exports.PurchaseModuleDto = exports.PaymentIdDto = exports.InitialPaymentDto = exports.GetPaymentsQueryDto = exports.CancelSubscriptionDto = exports.withAuditBatch = exports.GetAuditTrailDto = exports.IsNullable = exports.EmptyToNull = exports.SupabaseClientError = exports.createSupabaseServiceClient = exports.SupabaseServiceClient = exports.SignInDto = exports.SendPasswordResetEmailDto = exports.ResetPasswordDto = exports.ExchangeTokenHashDto = exports.ExchangeRefreshTokenDto = exports.createSupabaseAuthClient = exports.SupabaseAuthClient = exports.createSupabaseClient = exports.SupabaseClient = void 0;
|
|
11
|
+
exports.initialUntil = exports.cyclePeriodFor = exports.countWorkingDays = exports.completedMonths = exports.addMonths = exports.GetPublicHolidaysDto = exports.UpdateLeaveTypeDto = exports.UpdateLeaveRequestDto = exports.UpdateLeaveEntitlementDto = exports.ReviewLeaveRequestDto = exports.LeaveTypeIdDto = exports.LeaveRequestIdDto = exports.LeaveEntitlementIdDto = exports.GetLeaveTypesDto = exports.GetLeaveRequestsDto = exports.GetLeaveEntitlementsDto = exports.GetLeaveCalendarDto = exports.CreateLeaveTypeDto = exports.CreateLeaveRequestDto = exports.CreateLeaveEntitlementDto = exports.CreateLeaveAdjustmentDto = exports.BulkCreateLeaveEntitlementsDto = exports.UpdateTaskDto = exports.TaskLinkIdDto = exports.TaskIdDto = exports.GetTasksDto = exports.CreateTaskDto = exports.AddTaskLinkDto = exports.UpdateProjectDto = exports.ProjectIdDto = exports.GetProjectsDto = exports.CreateProjectDto = exports.UpdateLeadDto = exports.LeadIdDto = exports.GetLeadsDto = exports.CreateLeadDto = exports.ConvertLeadDto = exports.UpdateClientDto = exports.GetClientsDto = exports.CreateClientDto = exports.ClientIdDto = exports.SupportingFileNamesDto = exports.SupportingFileIdDto = exports.GetSupportingFilesDto = exports.UserIdDto = exports.UpdateUserDto = exports.UpdateMyProfileDto = exports.GetUsersQueryDto = exports.AddUserDto = exports.UpdatePermissionsDto = void 0;
|
|
12
|
+
exports.monthlyTotalMinor = exports.weekStartOf = exports.minutesBetween = exports.UpdateTimesheetEntryDto = exports.TimesheetIdDto = exports.TimesheetEntryIdDto = exports.ReviewTimesheetDto = exports.GetTimesheetsDto = exports.GetTimesheetReportDto = exports.CreateTimesheetEntryDto = exports.todayInJohannesburg = exports.shiftDays = exports.proRataDays = exports.isWithinPeriod = void 0;
|
|
13
13
|
var client_1 = require("./client");
|
|
14
14
|
Object.defineProperty(exports, "SupabaseClient", { enumerable: true, get: function () { return client_1.SupabaseClient; } });
|
|
15
15
|
Object.defineProperty(exports, "createSupabaseClient", { enumerable: true, get: function () { return client_1.createSupabaseClient; } });
|
|
@@ -32,6 +32,7 @@ Object.defineProperty(exports, "EmptyToNull", { enumerable: true, get: function
|
|
|
32
32
|
Object.defineProperty(exports, "IsNullable", { enumerable: true, get: function () { return validators_1.IsNullable; } });
|
|
33
33
|
var audit_1 = require("./audit");
|
|
34
34
|
Object.defineProperty(exports, "GetAuditTrailDto", { enumerable: true, get: function () { return audit_1.GetAuditTrailDto; } });
|
|
35
|
+
Object.defineProperty(exports, "withAuditBatch", { enumerable: true, get: function () { return audit_1.withAuditBatch; } });
|
|
35
36
|
var billing_1 = require("./billing");
|
|
36
37
|
Object.defineProperty(exports, "CancelSubscriptionDto", { enumerable: true, get: function () { return billing_1.CancelSubscriptionDto; } });
|
|
37
38
|
Object.defineProperty(exports, "GetPaymentsQueryDto", { enumerable: true, get: function () { return billing_1.GetPaymentsQueryDto; } });
|
package/dist/leads/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { MmsList } from '@managemint-solutions/entities/common';
|
|
|
3
3
|
import type { LeadEntity, LeadSummary } from '@managemint-solutions/entities/leads';
|
|
4
4
|
import type { AuthContext } from '../auth';
|
|
5
5
|
import type { AdditionalDetailsResource } from '../additional-details';
|
|
6
|
-
import { type AuditResource } from '../audit';
|
|
6
|
+
import { EntityAuditTrail, type AuditResource } from '../audit';
|
|
7
7
|
import type { ClientsResource } from '../clients';
|
|
8
8
|
import type { TypedSupabaseClient } from '../client';
|
|
9
9
|
import type { ConvertLeadDto, CreateLeadDto, GetLeadsDto, UpdateLeadDto } from './dto';
|
|
@@ -11,9 +11,10 @@ export * from './dto';
|
|
|
11
11
|
export declare class LeadsResource {
|
|
12
12
|
private readonly supabase;
|
|
13
13
|
private readonly auth;
|
|
14
|
-
private readonly audit;
|
|
15
14
|
private readonly additionalDetails;
|
|
16
15
|
private readonly table;
|
|
16
|
+
/** This record's history - its own rows and the rows that hang off it. */
|
|
17
|
+
readonly audit: EntityAuditTrail;
|
|
17
18
|
constructor(supabase: TypedSupabaseClient, auth: AuthContext, audit: AuditResource, additionalDetails: AdditionalDetailsResource);
|
|
18
19
|
list(query: GetLeadsDto): Promise<MmsList<LeadSummary>>;
|
|
19
20
|
getById(leadId: string): Promise<LeadEntity>;
|
package/dist/leads/index.js
CHANGED
|
@@ -106,18 +106,19 @@ const refuse = (status, message) => new errors_1.SupabaseClientError({ status, e
|
|
|
106
106
|
class LeadsResource {
|
|
107
107
|
supabase;
|
|
108
108
|
auth;
|
|
109
|
-
audit;
|
|
110
109
|
additionalDetails;
|
|
111
110
|
table;
|
|
111
|
+
/** This record's history - its own rows and the rows that hang off it. */
|
|
112
|
+
audit;
|
|
112
113
|
constructor(supabase, auth, audit, additionalDetails) {
|
|
113
114
|
this.supabase = supabase;
|
|
114
115
|
this.auth = auth;
|
|
115
|
-
this.audit = audit;
|
|
116
116
|
this.additionalDetails = additionalDetails;
|
|
117
117
|
this.table = (0, audit_1.auditedTable)(supabase, auth, audit, {
|
|
118
118
|
table: 'leads',
|
|
119
119
|
entityType: enum_6.AuditTrailEntityType.LEADS,
|
|
120
120
|
});
|
|
121
|
+
this.audit = new audit_1.EntityAuditTrail(supabase, auth, enum_6.AuditTrailEntityType.LEADS);
|
|
121
122
|
}
|
|
122
123
|
async list(query) {
|
|
123
124
|
const page = query.page;
|
|
@@ -27,6 +27,7 @@ class LeaveAdjustmentsResource {
|
|
|
27
27
|
this.table = (0, audit_1.auditedTable)(supabase, auth, audit, {
|
|
28
28
|
table: 'leave_adjustments',
|
|
29
29
|
entityType: enum_1.AuditTrailEntityType.LEAVE_ADJUSTMENTS,
|
|
30
|
+
related: { entityType: enum_1.AuditTrailEntityType.LEAVE_ENTITLEMENTS, column: 'leave_entitlement_id' },
|
|
30
31
|
});
|
|
31
32
|
}
|
|
32
33
|
/** The adjustments of the given entitlements, oldest first. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { MmsList } from '@managemint-solutions/entities/common';
|
|
2
2
|
import type { LeaveEntitlementEntity, LeavePeriod } from '@managemint-solutions/entities/leave';
|
|
3
3
|
import { LeaveGrantMethod } from '@managemint-solutions/entities/leave/enum';
|
|
4
|
-
import { type AuditActor, type AuditResource } from '../audit';
|
|
4
|
+
import { EntityAuditTrail, type AuditActor, type AuditResource } from '../audit';
|
|
5
5
|
import type { TypedSupabaseClient } from '../client';
|
|
6
6
|
import type { GetLeaveEntitlementsDto, UpdateLeaveEntitlementDto } from './dto';
|
|
7
7
|
/** The create input after the api has resolved the defaults and copied the type's policy on. */
|
|
@@ -21,6 +21,8 @@ export declare class LeaveEntitlementsResource {
|
|
|
21
21
|
private readonly supabase;
|
|
22
22
|
private readonly auth;
|
|
23
23
|
private readonly table;
|
|
24
|
+
/** This record's history - its own rows and the rows that hang off it. */
|
|
25
|
+
readonly audit: EntityAuditTrail;
|
|
24
26
|
constructor(supabase: TypedSupabaseClient, auth: AuditActor, audit: AuditResource);
|
|
25
27
|
list(query: GetLeaveEntitlementsDto): Promise<MmsList<LeaveEntitlementEntity>>;
|
|
26
28
|
getById(leaveEntitlementId: string): Promise<LeaveEntitlementEntity>;
|
|
@@ -38,6 +38,8 @@ class LeaveEntitlementsResource {
|
|
|
38
38
|
supabase;
|
|
39
39
|
auth;
|
|
40
40
|
table;
|
|
41
|
+
/** This record's history - its own rows and the rows that hang off it. */
|
|
42
|
+
audit;
|
|
41
43
|
constructor(supabase, auth, audit) {
|
|
42
44
|
this.supabase = supabase;
|
|
43
45
|
this.auth = auth;
|
|
@@ -45,6 +47,7 @@ class LeaveEntitlementsResource {
|
|
|
45
47
|
table: 'leave_entitlements',
|
|
46
48
|
entityType: enum_1.AuditTrailEntityType.LEAVE_ENTITLEMENTS,
|
|
47
49
|
});
|
|
50
|
+
this.audit = new audit_1.EntityAuditTrail(supabase, auth, enum_1.AuditTrailEntityType.LEAVE_ENTITLEMENTS);
|
|
48
51
|
}
|
|
49
52
|
async list(query) {
|
|
50
53
|
const page = query.page;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { MmsList } from '@managemint-solutions/entities/common';
|
|
2
2
|
import type { LeaveCalendarEntry, LeaveRequestEntity } from '@managemint-solutions/entities/leave';
|
|
3
3
|
import { LeaveRequestStatus, LeaveReviewDecision } from '@managemint-solutions/entities/leave/enum';
|
|
4
|
-
import { type AuditActor, type AuditResource } from '../audit';
|
|
4
|
+
import { EntityAuditTrail, type AuditActor, type AuditResource } from '../audit';
|
|
5
5
|
import type { TypedSupabaseClient } from '../client';
|
|
6
6
|
import type { GetLeaveCalendarDto, GetLeaveRequestsDto } from './dto';
|
|
7
7
|
/** A request as the api inserts it: the caller's own, with the days already counted. */
|
|
@@ -39,6 +39,8 @@ export declare class LeaveRequestsResource {
|
|
|
39
39
|
private readonly supabase;
|
|
40
40
|
private readonly auth;
|
|
41
41
|
private readonly table;
|
|
42
|
+
/** This record's history - its own rows and the rows that hang off it. */
|
|
43
|
+
readonly audit: EntityAuditTrail;
|
|
42
44
|
constructor(supabase: TypedSupabaseClient, auth: AuditActor, audit: AuditResource);
|
|
43
45
|
list(query: GetLeaveRequestsDto, options?: ListOptions): Promise<MmsList<LeaveRequestEntity>>;
|
|
44
46
|
getById(leaveRequestId: string): Promise<LeaveRequestEntity>;
|
|
@@ -49,6 +49,8 @@ class LeaveRequestsResource {
|
|
|
49
49
|
supabase;
|
|
50
50
|
auth;
|
|
51
51
|
table;
|
|
52
|
+
/** This record's history - its own rows and the rows that hang off it. */
|
|
53
|
+
audit;
|
|
52
54
|
constructor(supabase, auth, audit) {
|
|
53
55
|
this.supabase = supabase;
|
|
54
56
|
this.auth = auth;
|
|
@@ -56,6 +58,7 @@ class LeaveRequestsResource {
|
|
|
56
58
|
table: 'leave_requests',
|
|
57
59
|
entityType: enum_1.AuditTrailEntityType.LEAVE_REQUESTS,
|
|
58
60
|
});
|
|
61
|
+
this.audit = new audit_1.EntityAuditTrail(supabase, auth, enum_1.AuditTrailEntityType.LEAVE_REQUESTS);
|
|
59
62
|
}
|
|
60
63
|
async list(query, options = {}) {
|
|
61
64
|
const page = query.page;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AuditActor } from '../audit';
|
|
2
2
|
import type { TypedSupabaseClient } from '../client';
|
|
3
3
|
/**
|
|
4
4
|
* Everything Paystack knows an organization by, plus the two secrets the billing service acts
|
|
@@ -31,15 +31,14 @@ export type PaymentMethodLookupColumn = 'paystack_customer_code' | 'paystack_sub
|
|
|
31
31
|
* policy. Only the service role can reach it.
|
|
32
32
|
*
|
|
33
33
|
* Service-role only, like the invites - this lives on the organization scope of the service
|
|
34
|
-
* client and never on a caller's client. Writes are audited
|
|
35
|
-
*
|
|
36
|
-
*
|
|
34
|
+
* client and never on a caller's client. Writes are not audited: every column is provider
|
|
35
|
+
* machinery, and an organization's subscription history is the billing ledger, not its audit
|
|
36
|
+
* trail (`untracked` on the organizations table, docs/11-audit-trail.md).
|
|
37
37
|
*/
|
|
38
38
|
export declare class OrganizationPaymentMethodsResource {
|
|
39
39
|
private readonly supabase;
|
|
40
40
|
private readonly actor;
|
|
41
|
-
|
|
42
|
-
constructor(supabase: TypedSupabaseClient, actor: AuditActor, audit: AuditResource);
|
|
41
|
+
constructor(supabase: TypedSupabaseClient, actor: AuditActor);
|
|
43
42
|
/** The actor organization's payment method; every column null when nothing has been stored yet. */
|
|
44
43
|
get(): Promise<OrganizationPaymentMethod>;
|
|
45
44
|
/**
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OrganizationPaymentMethodsResource = exports.PAYMENT_METHOD_COLUMNS = void 0;
|
|
4
|
-
const enum_1 = require("@managemint-solutions/entities/audit-trail/enum");
|
|
5
|
-
const audit_1 = require("../audit");
|
|
6
4
|
const errors_1 = require("../errors");
|
|
7
5
|
exports.PAYMENT_METHOD_COLUMNS = [
|
|
8
6
|
'paystack_customer_code',
|
|
@@ -33,18 +31,16 @@ const EMPTY = {
|
|
|
33
31
|
* policy. Only the service role can reach it.
|
|
34
32
|
*
|
|
35
33
|
* Service-role only, like the invites - this lives on the organization scope of the service
|
|
36
|
-
* client and never on a caller's client. Writes are audited
|
|
37
|
-
*
|
|
38
|
-
*
|
|
34
|
+
* client and never on a caller's client. Writes are not audited: every column is provider
|
|
35
|
+
* machinery, and an organization's subscription history is the billing ledger, not its audit
|
|
36
|
+
* trail (`untracked` on the organizations table, docs/11-audit-trail.md).
|
|
39
37
|
*/
|
|
40
38
|
class OrganizationPaymentMethodsResource {
|
|
41
39
|
supabase;
|
|
42
40
|
actor;
|
|
43
|
-
|
|
44
|
-
constructor(supabase, actor, audit) {
|
|
41
|
+
constructor(supabase, actor) {
|
|
45
42
|
this.supabase = supabase;
|
|
46
43
|
this.actor = actor;
|
|
47
|
-
this.audit = audit;
|
|
48
44
|
}
|
|
49
45
|
/** The actor organization's payment method; every column null when nothing has been stored yet. */
|
|
50
46
|
async get() {
|
|
@@ -73,15 +69,6 @@ class OrganizationPaymentMethodsResource {
|
|
|
73
69
|
}, { onConflict: 'organization_id' });
|
|
74
70
|
if (error)
|
|
75
71
|
throw (0, errors_1.mapPostgrestError)(error);
|
|
76
|
-
const changes = (0, audit_1.diffChanges)(before, after);
|
|
77
|
-
if (Object.keys(changes).length === 0)
|
|
78
|
-
return;
|
|
79
|
-
await this.audit.record({
|
|
80
|
-
action: enum_1.AuditTrailAction.UPDATE,
|
|
81
|
-
entityType: enum_1.AuditTrailEntityType.ORGANIZATION,
|
|
82
|
-
entityId: this.actor.organization_id,
|
|
83
|
-
changes,
|
|
84
|
-
});
|
|
85
72
|
}
|
|
86
73
|
}
|
|
87
74
|
exports.OrganizationPaymentMethodsResource = OrganizationPaymentMethodsResource;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SeatReservation } from '@managemint-solutions/entities/billing';
|
|
2
|
-
import { type AuditActor, type AuditResource, type AuditRow } from '../audit';
|
|
2
|
+
import { type AuditActor, type AuditResource, type AuditRow, EntityAuditTrail } from '../audit';
|
|
3
3
|
import type { TypedSupabaseClient } from '../client';
|
|
4
4
|
export * from './dto';
|
|
5
5
|
/** The organizations row as `ORGANIZATION_SELECT` produces it, before the api shapes it. */
|
|
@@ -18,6 +18,8 @@ export declare class OrganizationsResource {
|
|
|
18
18
|
private readonly supabase;
|
|
19
19
|
private readonly auth;
|
|
20
20
|
private readonly table;
|
|
21
|
+
/** This record's history - its own rows and the rows that hang off it. */
|
|
22
|
+
readonly audit: EntityAuditTrail;
|
|
21
23
|
constructor(supabase: TypedSupabaseClient, auth: AuditActor, audit: AuditResource);
|
|
22
24
|
getById(organizationId: string): Promise<OrganizationRow>;
|
|
23
25
|
/** The caller's own organization, from the auth context. */
|
|
@@ -20,12 +20,34 @@ const audit_1 = require("../audit");
|
|
|
20
20
|
const errors_1 = require("../errors");
|
|
21
21
|
const transforms_1 = require("../transforms");
|
|
22
22
|
__exportStar(require("./dto"), exports);
|
|
23
|
+
// The subscription machinery on the row - what the billing flows and the Paystack webhooks
|
|
24
|
+
// move. Its history is the billing ledger (billing_transactions, invoices, billing_events);
|
|
25
|
+
// the organization's audit trail is its profile, so these are never recorded there.
|
|
26
|
+
const SUBSCRIPTION_COLUMNS = [
|
|
27
|
+
'billing_status',
|
|
28
|
+
'billing_status_updated_at',
|
|
29
|
+
'billing_cycle_locked',
|
|
30
|
+
'payment_frequency',
|
|
31
|
+
'payment_override',
|
|
32
|
+
'last_payment_date',
|
|
33
|
+
'next_payment_date',
|
|
34
|
+
'access_expires_at',
|
|
35
|
+
'active_seat_count',
|
|
36
|
+
'paid_seat_count',
|
|
37
|
+
'modules',
|
|
38
|
+
'pricebook_id',
|
|
39
|
+
'card_brand',
|
|
40
|
+
'card_description',
|
|
41
|
+
'card_expiry_date',
|
|
42
|
+
'card_expiring_soon',
|
|
43
|
+
];
|
|
23
44
|
// The organizations row *is* the organization: it has no organization_id column, hence
|
|
24
45
|
// `scopeByOrganization: false` wherever it is audited.
|
|
25
46
|
const ORGANIZATIONS_TABLE = {
|
|
26
47
|
table: 'organizations',
|
|
27
48
|
entityType: enum_1.AuditTrailEntityType.ORGANIZATION,
|
|
28
49
|
scopeByOrganization: false,
|
|
50
|
+
untracked: SUBSCRIPTION_COLUMNS,
|
|
29
51
|
};
|
|
30
52
|
// The api maps this row into OrganizationEntity: the module keys stored here are priced
|
|
31
53
|
// against the organization's pricebook, and the flat address columns are nested. That is
|
|
@@ -81,10 +103,13 @@ class OrganizationsResource {
|
|
|
81
103
|
supabase;
|
|
82
104
|
auth;
|
|
83
105
|
table;
|
|
106
|
+
/** This record's history - its own rows and the rows that hang off it. */
|
|
107
|
+
audit;
|
|
84
108
|
constructor(supabase, auth, audit) {
|
|
85
109
|
this.supabase = supabase;
|
|
86
110
|
this.auth = auth;
|
|
87
111
|
this.table = (0, audit_1.auditedTable)(supabase, auth, audit, ORGANIZATIONS_TABLE);
|
|
112
|
+
this.audit = new audit_1.EntityAuditTrail(supabase, auth, enum_1.AuditTrailEntityType.ORGANIZATION);
|
|
88
113
|
}
|
|
89
114
|
async getById(organizationId) {
|
|
90
115
|
const { data, error } = await this.supabase
|
|
@@ -203,11 +228,14 @@ class OrganizationSignupResource {
|
|
|
203
228
|
if (error)
|
|
204
229
|
throw (0, errors_1.mapPostgrestError)(error);
|
|
205
230
|
const row = data;
|
|
231
|
+
const changes = (0, audit_1.snapshotChanges)(row, 'new');
|
|
232
|
+
for (const column of SUBSCRIPTION_COLUMNS)
|
|
233
|
+
delete changes[column];
|
|
206
234
|
await this.auditFor(row.mms_id).record({
|
|
207
235
|
action: enum_1.AuditTrailAction.INSERT,
|
|
208
236
|
entityType: enum_1.AuditTrailEntityType.ORGANIZATION,
|
|
209
237
|
entityId: row.mms_id,
|
|
210
|
-
changes
|
|
238
|
+
changes,
|
|
211
239
|
});
|
|
212
240
|
return (0, audit_1.withoutInternalId)(row);
|
|
213
241
|
}
|
|
@@ -22,6 +22,8 @@ __exportStar(require("./dto"), exports);
|
|
|
22
22
|
const PERMISSIONS_TABLE = {
|
|
23
23
|
table: 'permissions',
|
|
24
24
|
entityType: enum_1.AuditTrailEntityType.PERMISSION,
|
|
25
|
+
// A permissions change shows in the history of the user it belongs to.
|
|
26
|
+
related: { entityType: enum_1.AuditTrailEntityType.USER, column: 'user_id' },
|
|
25
27
|
};
|
|
26
28
|
/**
|
|
27
29
|
* The `permissions` table. Rows are created by the `on_auth_user_created` trigger and deleted
|
package/dist/projects/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { MmsList } from '@managemint-solutions/entities/common';
|
|
2
2
|
import type { ProjectEntity, ProjectSummary } from '@managemint-solutions/entities/projects';
|
|
3
3
|
import type { AuthContext } from '../auth';
|
|
4
|
-
import { type AuditResource } from '../audit';
|
|
4
|
+
import { EntityAuditTrail, type AuditResource } from '../audit';
|
|
5
5
|
import type { TypedSupabaseClient } from '../client';
|
|
6
6
|
import type { CreateProjectDto, GetProjectsDto, UpdateProjectDto } from './dto';
|
|
7
7
|
export * from './dto';
|
|
@@ -9,6 +9,8 @@ export declare class ProjectsResource {
|
|
|
9
9
|
private readonly supabase;
|
|
10
10
|
private readonly auth;
|
|
11
11
|
private readonly table;
|
|
12
|
+
/** This record's history - its own rows and the rows that hang off it. */
|
|
13
|
+
readonly audit: EntityAuditTrail;
|
|
12
14
|
constructor(supabase: TypedSupabaseClient, auth: AuthContext, audit: AuditResource);
|
|
13
15
|
list(query: GetProjectsDto): Promise<MmsList<ProjectSummary>>;
|
|
14
16
|
getById(projectId: string): Promise<ProjectEntity>;
|
package/dist/projects/index.js
CHANGED
|
@@ -79,6 +79,8 @@ class ProjectsResource {
|
|
|
79
79
|
supabase;
|
|
80
80
|
auth;
|
|
81
81
|
table;
|
|
82
|
+
/** This record's history - its own rows and the rows that hang off it. */
|
|
83
|
+
audit;
|
|
82
84
|
constructor(supabase, auth, audit) {
|
|
83
85
|
this.supabase = supabase;
|
|
84
86
|
this.auth = auth;
|
|
@@ -86,6 +88,7 @@ class ProjectsResource {
|
|
|
86
88
|
table: 'projects',
|
|
87
89
|
entityType: enum_1.AuditTrailEntityType.PROJECTS,
|
|
88
90
|
});
|
|
91
|
+
this.audit = new audit_1.EntityAuditTrail(supabase, auth, enum_1.AuditTrailEntityType.PROJECTS);
|
|
89
92
|
}
|
|
90
93
|
async list(query) {
|
|
91
94
|
const page = query.page;
|
|
@@ -119,6 +119,13 @@ export declare class SupabaseServiceClient {
|
|
|
119
119
|
user_id: string;
|
|
120
120
|
organization_id: string;
|
|
121
121
|
} | null>;
|
|
122
|
+
/**
|
|
123
|
+
* Deletes every audit row written before `cutoff` and says how many went - the daily
|
|
124
|
+
* retention cron. Every tenant at once: the period is the same for all of them. Batched by
|
|
125
|
+
* the internal identity, oldest first, so no single statement holds a long lock, and each
|
|
126
|
+
* delete is a range rather than a list of ids.
|
|
127
|
+
*/
|
|
128
|
+
purgeAuditLogsBefore(cutoff: string): Promise<number>;
|
|
122
129
|
/**
|
|
123
130
|
* A ledger row by id alone. Also unscoped by necessity: a `charge.success` carries the
|
|
124
131
|
* transaction id in its metadata, and the row is what says which tenant it belongs to.
|
|
@@ -49,7 +49,7 @@ class SupabaseOrganizationScope {
|
|
|
49
49
|
this.leaveEntitlements = new leave_1.LeaveEntitlementsResource(supabase, actor, this.audit);
|
|
50
50
|
this.leaveRequests = new leave_1.LeaveRequestsResource(supabase, actor, this.audit);
|
|
51
51
|
this.organizations = new organizations_1.OrganizationsResource(supabase, actor, this.audit);
|
|
52
|
-
this.paymentMethod = new organization_payment_methods_1.OrganizationPaymentMethodsResource(supabase, actor
|
|
52
|
+
this.paymentMethod = new organization_payment_methods_1.OrganizationPaymentMethodsResource(supabase, actor);
|
|
53
53
|
this.permissions = new permissions_1.PermissionsResource(supabase, actor, this.audit);
|
|
54
54
|
this.users = new users_1.UsersResource(supabase, actor, this.audit);
|
|
55
55
|
}
|
|
@@ -209,6 +209,40 @@ class SupabaseServiceClient {
|
|
|
209
209
|
throw (0, errors_1.mapPostgrestError)(error);
|
|
210
210
|
return (data ?? null);
|
|
211
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Deletes every audit row written before `cutoff` and says how many went - the daily
|
|
214
|
+
* retention cron. Every tenant at once: the period is the same for all of them. Batched by
|
|
215
|
+
* the internal identity, oldest first, so no single statement holds a long lock, and each
|
|
216
|
+
* delete is a range rather than a list of ids.
|
|
217
|
+
*/
|
|
218
|
+
async purgeAuditLogsBefore(cutoff) {
|
|
219
|
+
// Supabase's default `max_rows`: a larger limit would come back short and end the loop early.
|
|
220
|
+
const batchSize = 1000;
|
|
221
|
+
let deleted = 0;
|
|
222
|
+
for (;;) {
|
|
223
|
+
const { data, error } = await this.supabase
|
|
224
|
+
.from('audit_logs')
|
|
225
|
+
.select('id')
|
|
226
|
+
.lt('created_at', cutoff)
|
|
227
|
+
.order('id', { ascending: true })
|
|
228
|
+
.limit(batchSize);
|
|
229
|
+
if (error)
|
|
230
|
+
throw (0, errors_1.mapPostgrestError)(error);
|
|
231
|
+
const batch = (data ?? []);
|
|
232
|
+
if (batch.length === 0)
|
|
233
|
+
return deleted;
|
|
234
|
+
const { error: deleteError } = await this.supabase
|
|
235
|
+
.from('audit_logs')
|
|
236
|
+
.delete()
|
|
237
|
+
.lt('created_at', cutoff)
|
|
238
|
+
.lte('id', batch[batch.length - 1].id);
|
|
239
|
+
if (deleteError)
|
|
240
|
+
throw (0, errors_1.mapPostgrestError)(deleteError);
|
|
241
|
+
deleted += batch.length;
|
|
242
|
+
if (batch.length < batchSize)
|
|
243
|
+
return deleted;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
212
246
|
/**
|
|
213
247
|
* A ledger row by id alone. Also unscoped by necessity: a `charge.success` carries the
|
|
214
248
|
* transaction id in its metadata, and the row is what says which tenant it belongs to.
|
package/dist/tasks/index.d.ts
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import type { MmsList } from '@managemint-solutions/entities/common';
|
|
2
2
|
import type { TaskEntity, TaskSummary } from '@managemint-solutions/entities/tasks';
|
|
3
3
|
import type { AuthContext } from '../auth';
|
|
4
|
-
import { type AuditResource } from '../audit';
|
|
4
|
+
import { EntityAuditTrail, type AuditResource } from '../audit';
|
|
5
5
|
import type { TypedSupabaseClient } from '../client';
|
|
6
6
|
import type { AddTaskLinkDto, CreateTaskDto, GetTasksDto, UpdateTaskDto } from './dto';
|
|
7
7
|
export * from './dto';
|
|
8
8
|
export declare class TasksResource {
|
|
9
9
|
private readonly supabase;
|
|
10
10
|
private readonly auth;
|
|
11
|
-
private readonly
|
|
11
|
+
private readonly auditLog;
|
|
12
12
|
private readonly table;
|
|
13
|
-
|
|
13
|
+
/** This record's history - its own rows and the rows that hang off it. */
|
|
14
|
+
readonly audit: EntityAuditTrail;
|
|
15
|
+
constructor(supabase: TypedSupabaseClient, auth: AuthContext, auditLog: AuditResource);
|
|
14
16
|
list(query: GetTasksDto): Promise<MmsList<TaskSummary>>;
|
|
15
17
|
getById(taskId: string): Promise<TaskEntity>;
|
|
16
18
|
create(input: CreateTaskDto): Promise<TaskEntity>;
|
|
@@ -43,11 +45,12 @@ export declare class TasksResource {
|
|
|
43
45
|
private checkLabels;
|
|
44
46
|
/**
|
|
45
47
|
* Makes `labelIds` the task's whole label set. The join rows carry no audit of their own: the
|
|
46
|
-
* change is
|
|
47
|
-
* only when the set actually moved -
|
|
48
|
-
*
|
|
49
|
-
* deleted, so should the trigger still refuse one (a label
|
|
50
|
-
* task's existing labels are untouched, and a label kept
|
|
48
|
+
* change is an UPDATE entry on the task, `labels: { old: [names], new: [names] }`, recorded
|
|
49
|
+
* only when the set actually moved - inside a request's audit batch it folds into the task's
|
|
50
|
+
* own entry for that save (`withAuditBatch`). Only the difference is written, the new labels
|
|
51
|
+
* inserted before the dropped ones are deleted, so should the trigger still refuse one (a label
|
|
52
|
+
* deleted after `checkLabels`) the task's existing labels are untouched, and a label kept
|
|
53
|
+
* across an edit keeps its place.
|
|
51
54
|
*/
|
|
52
55
|
private setLabels;
|
|
53
56
|
}
|
package/dist/tasks/index.js
CHANGED
|
@@ -124,16 +124,19 @@ const linkedTasks = (rows, linkType) => rows
|
|
|
124
124
|
class TasksResource {
|
|
125
125
|
supabase;
|
|
126
126
|
auth;
|
|
127
|
-
|
|
127
|
+
auditLog;
|
|
128
128
|
table;
|
|
129
|
-
|
|
129
|
+
/** This record's history - its own rows and the rows that hang off it. */
|
|
130
|
+
audit;
|
|
131
|
+
constructor(supabase, auth, auditLog) {
|
|
130
132
|
this.supabase = supabase;
|
|
131
133
|
this.auth = auth;
|
|
132
|
-
this.
|
|
133
|
-
this.table = (0, audit_1.auditedTable)(supabase, auth,
|
|
134
|
+
this.auditLog = auditLog;
|
|
135
|
+
this.table = (0, audit_1.auditedTable)(supabase, auth, auditLog, {
|
|
134
136
|
table: 'tasks',
|
|
135
137
|
entityType: enum_3.AuditTrailEntityType.TASKS,
|
|
136
138
|
});
|
|
139
|
+
this.audit = new audit_1.EntityAuditTrail(supabase, auth, enum_3.AuditTrailEntityType.TASKS);
|
|
137
140
|
}
|
|
138
141
|
async list(query) {
|
|
139
142
|
const page = query.page;
|
|
@@ -356,7 +359,7 @@ class TasksResource {
|
|
|
356
359
|
throw (0, errors_1.mapPostgrestError)(error);
|
|
357
360
|
}
|
|
358
361
|
const role = input.link_type === enum_1.TaskLinkType.CHILD ? 'subtask' : 'related';
|
|
359
|
-
await this.
|
|
362
|
+
await this.auditLog.record({
|
|
360
363
|
action: enum_3.AuditTrailAction.UPDATE,
|
|
361
364
|
entityType: enum_3.AuditTrailEntityType.TASKS,
|
|
362
365
|
entityId: taskId,
|
|
@@ -384,7 +387,7 @@ class TasksResource {
|
|
|
384
387
|
}
|
|
385
388
|
const madeHere = row.task_id === taskId;
|
|
386
389
|
const role = row.link_type === enum_1.TaskLinkType.RELATED ? 'related' : madeHere ? 'subtask' : 'parent';
|
|
387
|
-
await this.
|
|
390
|
+
await this.auditLog.record({
|
|
388
391
|
action: enum_3.AuditTrailAction.UPDATE,
|
|
389
392
|
entityType: enum_3.AuditTrailEntityType.TASKS,
|
|
390
393
|
entityId: taskId,
|
|
@@ -421,11 +424,12 @@ class TasksResource {
|
|
|
421
424
|
}
|
|
422
425
|
/**
|
|
423
426
|
* Makes `labelIds` the task's whole label set. The join rows carry no audit of their own: the
|
|
424
|
-
* change is
|
|
425
|
-
* only when the set actually moved -
|
|
426
|
-
*
|
|
427
|
-
* deleted, so should the trigger still refuse one (a label
|
|
428
|
-
* task's existing labels are untouched, and a label kept
|
|
427
|
+
* change is an UPDATE entry on the task, `labels: { old: [names], new: [names] }`, recorded
|
|
428
|
+
* only when the set actually moved - inside a request's audit batch it folds into the task's
|
|
429
|
+
* own entry for that save (`withAuditBatch`). Only the difference is written, the new labels
|
|
430
|
+
* inserted before the dropped ones are deleted, so should the trigger still refuse one (a label
|
|
431
|
+
* deleted after `checkLabels`) the task's existing labels are untouched, and a label kept
|
|
432
|
+
* across an edit keeps its place.
|
|
429
433
|
*/
|
|
430
434
|
async setLabels(taskId, labelIds) {
|
|
431
435
|
const { data: current, error: readError } = await this.supabase
|
|
@@ -467,7 +471,7 @@ class TasksResource {
|
|
|
467
471
|
if (error)
|
|
468
472
|
throw (0, errors_1.mapPostgrestError)(error);
|
|
469
473
|
}
|
|
470
|
-
await this.
|
|
474
|
+
await this.auditLog.record({
|
|
471
475
|
action: enum_3.AuditTrailAction.UPDATE,
|
|
472
476
|
entityType: enum_3.AuditTrailEntityType.TASKS,
|
|
473
477
|
entityId: taskId,
|
|
@@ -59,6 +59,7 @@ class TimesheetEntriesResource {
|
|
|
59
59
|
this.table = (0, audit_1.auditedTable)(supabase, auth, audit, {
|
|
60
60
|
table: 'timesheet_entries',
|
|
61
61
|
entityType: enum_1.AuditTrailEntityType.TIMESHEET_ENTRIES,
|
|
62
|
+
related: { entityType: enum_1.AuditTrailEntityType.TIMESHEETS, column: 'timesheet_id' },
|
|
62
63
|
});
|
|
63
64
|
}
|
|
64
65
|
async getById(timesheetEntryId) {
|
|
@@ -2,7 +2,7 @@ import type { MmsList } from '@managemint-solutions/entities/common';
|
|
|
2
2
|
import type { TimesheetEntity, TimesheetSummary } from '@managemint-solutions/entities/timesheets';
|
|
3
3
|
import { TimesheetReviewDecision, TimesheetStatus } from '@managemint-solutions/entities/timesheets/enum';
|
|
4
4
|
import type { AuthContext } from '../auth';
|
|
5
|
-
import { type AuditResource } from '../audit';
|
|
5
|
+
import { EntityAuditTrail, type AuditResource } from '../audit';
|
|
6
6
|
import type { TypedSupabaseClient } from '../client';
|
|
7
7
|
import type { GetTimesheetsDto } from './dto';
|
|
8
8
|
/** What the entry path needs of the week an entry falls in: its id and whether it is still open. */
|
|
@@ -29,6 +29,8 @@ export declare class TimesheetsResource {
|
|
|
29
29
|
private readonly supabase;
|
|
30
30
|
private readonly auth;
|
|
31
31
|
private readonly table;
|
|
32
|
+
/** This record's history - its own rows and the rows that hang off it. */
|
|
33
|
+
readonly audit: EntityAuditTrail;
|
|
32
34
|
constructor(supabase: TypedSupabaseClient, auth: AuthContext, audit: AuditResource);
|
|
33
35
|
list(query: GetTimesheetsDto, options?: ListOptions): Promise<MmsList<TimesheetSummary>>;
|
|
34
36
|
/** The week with its entries, by date then start time. */
|
|
@@ -27,6 +27,8 @@ class TimesheetsResource {
|
|
|
27
27
|
supabase;
|
|
28
28
|
auth;
|
|
29
29
|
table;
|
|
30
|
+
/** This record's history - its own rows and the rows that hang off it. */
|
|
31
|
+
audit;
|
|
30
32
|
constructor(supabase, auth, audit) {
|
|
31
33
|
this.supabase = supabase;
|
|
32
34
|
this.auth = auth;
|
|
@@ -34,6 +36,7 @@ class TimesheetsResource {
|
|
|
34
36
|
table: 'timesheets',
|
|
35
37
|
entityType: enum_1.AuditTrailEntityType.TIMESHEETS,
|
|
36
38
|
});
|
|
39
|
+
this.audit = new audit_1.EntityAuditTrail(supabase, auth, enum_1.AuditTrailEntityType.TIMESHEETS);
|
|
37
40
|
}
|
|
38
41
|
async list(query, options = {}) {
|
|
39
42
|
const page = query.page;
|
package/dist/users/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { MmsList } from '@managemint-solutions/entities/common';
|
|
2
2
|
import type { UserIdentity, UserSummary } from '@managemint-solutions/entities/users';
|
|
3
|
-
import { type AuditActor, type AuditResource, type AuditRow } from '../audit';
|
|
3
|
+
import { type AuditActor, type AuditResource, type AuditRow, EntityAuditTrail } from '../audit';
|
|
4
4
|
import type { TypedSupabaseClient } from '../client';
|
|
5
5
|
import type { GetUsersQueryDto } from './dto';
|
|
6
6
|
export * from './dto';
|
|
@@ -24,6 +24,8 @@ export declare class UsersResource {
|
|
|
24
24
|
private readonly supabase;
|
|
25
25
|
private readonly auth;
|
|
26
26
|
private readonly table;
|
|
27
|
+
/** This record's history - its own rows and the rows that hang off it. */
|
|
28
|
+
readonly audit: EntityAuditTrail;
|
|
27
29
|
constructor(supabase: TypedSupabaseClient, auth: AuditActor, audit: AuditResource);
|
|
28
30
|
list(query: GetUsersQueryDto): Promise<MmsList<UserSummary>>;
|
|
29
31
|
getRow(userId: string): Promise<UserRow>;
|
package/dist/users/index.js
CHANGED
|
@@ -33,10 +33,13 @@ class UsersResource {
|
|
|
33
33
|
supabase;
|
|
34
34
|
auth;
|
|
35
35
|
table;
|
|
36
|
+
/** This record's history - its own rows and the rows that hang off it. */
|
|
37
|
+
audit;
|
|
36
38
|
constructor(supabase, auth, audit) {
|
|
37
39
|
this.supabase = supabase;
|
|
38
40
|
this.auth = auth;
|
|
39
41
|
this.table = (0, audit_1.auditedTable)(supabase, auth, audit, USERS_TABLE);
|
|
42
|
+
this.audit = new audit_1.EntityAuditTrail(supabase, auth, enum_1.AuditTrailEntityType.USER);
|
|
40
43
|
}
|
|
41
44
|
async list(query) {
|
|
42
45
|
const page = query.page;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@managemint-solutions/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.53.0",
|
|
4
4
|
"description": "Typed Supabase data-access SDK for ManageMint Solutions",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "Scott Bebington <scottbebington@gmail.com>",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"testEnvironment": "node"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@managemint-solutions/entities": "^1.
|
|
54
|
+
"@managemint-solutions/entities": "^1.35.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"@nestjs/common": "^11.0.0",
|