Fix Garmin token storage and flaky e2e test
1. Increase garminOauth1Token and garminOauth2Token max length from 5000 to 20000 characters to accommodate encrypted OAuth tokens. Add logic to update existing field constraints in addUserFields(). 2. Fix flaky pocketbase-harness e2e test by adding retry logic with exponential backoff to createAdminUser() and createTestUser(). Handles SQLite database lock during PocketBase startup migrations. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,10 +6,12 @@ import PocketBase from "pocketbase";
|
||||
* Collection field definition for PocketBase.
|
||||
* For relation fields, collectionId/maxSelect/cascadeDelete are top-level properties.
|
||||
*/
|
||||
interface CollectionField {
|
||||
export interface CollectionField {
|
||||
name: string;
|
||||
type: string;
|
||||
required?: boolean;
|
||||
// Text field max length (PocketBase defaults to 5000 if not specified)
|
||||
max?: number;
|
||||
// Relation field properties (top-level, not in options)
|
||||
collectionId?: string;
|
||||
maxSelect?: number;
|
||||
@@ -142,10 +144,10 @@ const REQUIRED_COLLECTIONS = [PERIOD_LOGS_COLLECTION, DAILY_LOGS_COLLECTION];
|
||||
* Custom fields to add to the users collection.
|
||||
* These are required for Garmin integration and app functionality.
|
||||
*/
|
||||
const USER_CUSTOM_FIELDS = [
|
||||
export const USER_CUSTOM_FIELDS: CollectionField[] = [
|
||||
{ name: "garminConnected", type: "bool" },
|
||||
{ name: "garminOauth1Token", type: "text" },
|
||||
{ name: "garminOauth2Token", type: "text" },
|
||||
{ name: "garminOauth1Token", type: "text", max: 20000 },
|
||||
{ name: "garminOauth2Token", type: "text", max: 20000 },
|
||||
{ name: "garminTokenExpiresAt", type: "date" },
|
||||
{ name: "calendarToken", type: "text" },
|
||||
{ name: "lastPeriodDate", type: "date" },
|
||||
@@ -156,36 +158,62 @@ const USER_CUSTOM_FIELDS = [
|
||||
];
|
||||
|
||||
/**
|
||||
* Adds custom fields to the users collection if they don't already exist.
|
||||
* Adds or updates custom fields on the users collection.
|
||||
* For new fields: adds them. For existing fields: updates max constraint if different.
|
||||
* This is idempotent - safe to run multiple times.
|
||||
*/
|
||||
export async function addUserFields(pb: PocketBase): Promise<void> {
|
||||
const usersCollection = await pb.collections.getOne("users");
|
||||
|
||||
// Get existing field names
|
||||
const existingFieldNames = new Set(
|
||||
(usersCollection.fields || []).map((f: { name: string }) => f.name),
|
||||
// Build a map of existing fields by name
|
||||
const existingFieldsMap = new Map<string, Record<string, unknown>>(
|
||||
(usersCollection.fields || []).map((f: Record<string, unknown>) => [
|
||||
f.name as string,
|
||||
f,
|
||||
]),
|
||||
);
|
||||
|
||||
// Filter to only new fields
|
||||
const newFields = USER_CUSTOM_FIELDS.filter(
|
||||
(f) => !existingFieldNames.has(f.name),
|
||||
);
|
||||
// Separate new fields from fields that need updating
|
||||
const newFields: CollectionField[] = [];
|
||||
const fieldsToUpdate: string[] = [];
|
||||
|
||||
if (newFields.length > 0) {
|
||||
// Combine existing fields with new ones
|
||||
for (const definedField of USER_CUSTOM_FIELDS) {
|
||||
const existingField = existingFieldsMap.get(definedField.name);
|
||||
if (!existingField) {
|
||||
newFields.push(definedField);
|
||||
} else if (
|
||||
definedField.max !== undefined &&
|
||||
existingField.max !== definedField.max
|
||||
) {
|
||||
fieldsToUpdate.push(definedField.name);
|
||||
existingField.max = definedField.max;
|
||||
}
|
||||
}
|
||||
|
||||
const hasChanges = newFields.length > 0 || fieldsToUpdate.length > 0;
|
||||
|
||||
if (hasChanges) {
|
||||
// Combine existing fields (with updates) and new fields
|
||||
const allFields = [...(usersCollection.fields || []), ...newFields];
|
||||
|
||||
await pb.collections.update(usersCollection.id, {
|
||||
fields: allFields,
|
||||
});
|
||||
|
||||
console.log(
|
||||
` Added ${newFields.length} field(s) to users:`,
|
||||
newFields.map((f) => f.name),
|
||||
);
|
||||
if (newFields.length > 0) {
|
||||
console.log(
|
||||
` Added ${newFields.length} field(s) to users:`,
|
||||
newFields.map((f) => f.name),
|
||||
);
|
||||
}
|
||||
if (fieldsToUpdate.length > 0) {
|
||||
console.log(
|
||||
` Updated max constraint for ${fieldsToUpdate.length} field(s):`,
|
||||
fieldsToUpdate,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.log(" All user fields already exist.");
|
||||
console.log(" All user fields already exist with correct settings.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user