mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 21:15:32 +09:00
refactor(push): retire the push service delivery experiment (#3000)
This commit is contained in:
1 parent
2a9e25c788
commit
5b280898c5
83 files changed
+1882
-10066
No files matched your search
@@ -23,10 +23,7 @@ import {
|
||||
GatewayRolloutConfigResponse,
|
||||
GatewayRolloutConfigUpdateRequest,
|
||||
} from '@fluxer/schema/src/domains/admin/GatewayRolloutSchemas';
|
||||
import {
|
||||
PushServiceDeliveryConfigResponse,
|
||||
PushServiceDeliveryConfigUpdateRequest,
|
||||
} from '@fluxer/schema/src/domains/admin/PushServiceDeliverySchemas';
|
||||
import {PushRelayConfigResponse, PushRelayConfigUpdateRequest} from '@fluxer/schema/src/domains/admin/PushRelaySchemas';
|
||||
import {
|
||||
VoiceNoiseSuppressionConfigResponse,
|
||||
VoiceNoiseSuppressionConfigUpdateRequest,
|
||||
@@ -656,7 +653,7 @@ export const InstanceConfigResponse = z.object({
|
||||
sso: SsoConfigResponse,
|
||||
gateway_rollout: GatewayRolloutConfigResponse,
|
||||
voice_noise_suppression: VoiceNoiseSuppressionConfigResponse,
|
||||
push_service_delivery: PushServiceDeliveryConfigResponse,
|
||||
push_relay: PushRelayConfigResponse,
|
||||
domain_migration: DomainMigrationConfigResponse,
|
||||
altcha_captcha: AltchaCaptchaConfigResponse,
|
||||
experiment_delivery: ExperimentDeliveryConfigResponse,
|
||||
@@ -695,7 +692,7 @@ const InstancePolicyUpdateSchema = z.object({
|
||||
export const InstanceConfigUpdateRequest = z.object({
|
||||
gateway_rollout: GatewayRolloutConfigUpdateRequest.nullish(),
|
||||
voice_noise_suppression: VoiceNoiseSuppressionConfigUpdateRequest.nullish(),
|
||||
push_service_delivery: PushServiceDeliveryConfigUpdateRequest.nullish(),
|
||||
push_relay: PushRelayConfigUpdateRequest.nullish(),
|
||||
domain_migration: DomainMigrationConfigUpdateRequest.nullish(),
|
||||
altcha_captcha: AltchaCaptchaConfigUpdateRequest.nullish(),
|
||||
experiment_delivery: ExperimentDeliveryConfigUpdateRequest.nullish(),
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {
|
||||
LegacyPushServiceDeliveryWire,
|
||||
PushRelayConfigSchema,
|
||||
PushRelayConfigUpdateRequest,
|
||||
toLegacyPushServiceDeliveryWire,
|
||||
} from '@fluxer/schema/src/domains/admin/PushRelaySchemas';
|
||||
import {describe, expect, test} from 'vitest';
|
||||
|
||||
const ADMIN_USER_ID = '1500000000000000001';
|
||||
const ACCEPTED_AT = '2026-09-27T10:11:12.000Z';
|
||||
|
||||
describe('push relay consent', () => {
|
||||
test('reads back as unaccepted by default', () => {
|
||||
expect(PushRelayConfigSchema.parse({})).toEqual({
|
||||
relay_consent_accepted: false,
|
||||
relay_consent_accepted_at: null,
|
||||
relay_consent_accepted_by: null,
|
||||
});
|
||||
});
|
||||
|
||||
test('a stored push service delivery row keeps its consent', () => {
|
||||
expect(
|
||||
PushRelayConfigSchema.parse({
|
||||
enabled: true,
|
||||
config_version: 12,
|
||||
rollout_basis_points: 10000,
|
||||
rollout_salt: 'push-service-delivery-v1',
|
||||
included_user_ids: [],
|
||||
excluded_user_ids: ['1500000000000000003'],
|
||||
relay_consent_accepted: true,
|
||||
relay_consent_accepted_at: ACCEPTED_AT,
|
||||
relay_consent_accepted_by: ADMIN_USER_ID,
|
||||
}),
|
||||
).toEqual({
|
||||
relay_consent_accepted: true,
|
||||
relay_consent_accepted_at: ACCEPTED_AT,
|
||||
relay_consent_accepted_by: ADMIN_USER_ID,
|
||||
});
|
||||
});
|
||||
|
||||
test('a stored row that predates relay consent reads back as not accepted', () => {
|
||||
expect(
|
||||
PushRelayConfigSchema.parse({
|
||||
enabled: true,
|
||||
config_version: 4,
|
||||
rollout_basis_points: 10000,
|
||||
rollout_salt: 'push-service-delivery-v1',
|
||||
included_user_ids: [],
|
||||
excluded_user_ids: [],
|
||||
}),
|
||||
).toEqual({
|
||||
relay_consent_accepted: false,
|
||||
relay_consent_accepted_at: null,
|
||||
relay_consent_accepted_by: null,
|
||||
});
|
||||
});
|
||||
|
||||
test('an accepted consent round-trips', () => {
|
||||
const accepted = {
|
||||
relay_consent_accepted: true,
|
||||
relay_consent_accepted_at: ACCEPTED_AT,
|
||||
relay_consent_accepted_by: ADMIN_USER_ID,
|
||||
};
|
||||
expect(PushRelayConfigSchema.parse(accepted)).toEqual(accepted);
|
||||
});
|
||||
|
||||
test('the update request takes the consent flag on its own', () => {
|
||||
expect(PushRelayConfigUpdateRequest.parse({relay_consent_accepted: true})).toEqual({
|
||||
relay_consent_accepted: true,
|
||||
});
|
||||
});
|
||||
|
||||
test('the update request drops a client-supplied acceptance stamp', () => {
|
||||
expect(
|
||||
PushRelayConfigUpdateRequest.parse({
|
||||
relay_consent_accepted: true,
|
||||
relay_consent_accepted_at: '2020-01-01T00:00:00.000Z',
|
||||
relay_consent_accepted_by: ADMIN_USER_ID,
|
||||
}),
|
||||
).toEqual({relay_consent_accepted: true});
|
||||
});
|
||||
|
||||
test.each([
|
||||
{relay_consent_accepted_at: 'yesterday'},
|
||||
{relay_consent_accepted_at: '2026-09-27'},
|
||||
{relay_consent_accepted_by: 'not-an-id'},
|
||||
{relay_consent_accepted: 'yes'},
|
||||
])('rejects a malformed stored consent: %j', (value) => {
|
||||
expect(PushRelayConfigSchema.safeParse(value).success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('legacy push service delivery wire', () => {
|
||||
test('pins every rollout field to full enrolment and keeps the consent and version', () => {
|
||||
const wire = toLegacyPushServiceDeliveryWire(
|
||||
{relay_consent_accepted: true, relay_consent_accepted_at: ACCEPTED_AT, relay_consent_accepted_by: ADMIN_USER_ID},
|
||||
7,
|
||||
);
|
||||
expect(wire).toEqual({
|
||||
enabled: true,
|
||||
config_version: 7,
|
||||
rollout_basis_points: 10000,
|
||||
rollout_salt: 'push-service-delivery-v1',
|
||||
included_user_ids: [],
|
||||
excluded_user_ids: [],
|
||||
relay_consent_accepted: true,
|
||||
relay_consent_accepted_at: ACCEPTED_AT,
|
||||
relay_consent_accepted_by: ADMIN_USER_ID,
|
||||
});
|
||||
expect(LegacyPushServiceDeliveryWire.parse(wire)).toEqual(wire);
|
||||
});
|
||||
|
||||
test('rejects a document that would take anyone off the push service', () => {
|
||||
const wire = toLegacyPushServiceDeliveryWire(PushRelayConfigSchema.parse({}), 0);
|
||||
expect(LegacyPushServiceDeliveryWire.safeParse({...wire, enabled: false}).success).toBe(false);
|
||||
expect(LegacyPushServiceDeliveryWire.safeParse({...wire, rollout_basis_points: 5000}).success).toBe(false);
|
||||
expect(LegacyPushServiceDeliveryWire.safeParse({...wire, excluded_user_ids: ['1500000000000000003']}).success).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {z} from 'zod';
|
||||
|
||||
const LEGACY_PUSH_SERVICE_DELIVERY_ROLLOUT_BASIS_POINTS = 10000;
|
||||
const LEGACY_PUSH_SERVICE_DELIVERY_SALT = 'push-service-delivery-v1';
|
||||
|
||||
export const PushRelayConfigSchema = z.object({
|
||||
relay_consent_accepted: z.boolean().default(false),
|
||||
relay_consent_accepted_at: z.iso.datetime().nullable().default(null),
|
||||
relay_consent_accepted_by: z
|
||||
.string()
|
||||
.regex(/^\d{1,20}$/u)
|
||||
.nullable()
|
||||
.default(null),
|
||||
});
|
||||
|
||||
export type PushRelayConfig = z.infer<typeof PushRelayConfigSchema>;
|
||||
|
||||
export const PushRelayConfigResponse = PushRelayConfigSchema;
|
||||
|
||||
export type PushRelayConfigResponse = z.infer<typeof PushRelayConfigResponse>;
|
||||
|
||||
export const PushRelayConfigUpdateRequest = z.object({
|
||||
relay_consent_accepted: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export type PushRelayConfigUpdateRequest = z.infer<typeof PushRelayConfigUpdateRequest>;
|
||||
|
||||
export const LegacyPushServiceDeliveryWire = PushRelayConfigSchema.extend({
|
||||
enabled: z.literal(true),
|
||||
config_version: z.number().int().min(0),
|
||||
rollout_basis_points: z.literal(LEGACY_PUSH_SERVICE_DELIVERY_ROLLOUT_BASIS_POINTS),
|
||||
rollout_salt: z.literal(LEGACY_PUSH_SERVICE_DELIVERY_SALT),
|
||||
included_user_ids: z.tuple([]),
|
||||
excluded_user_ids: z.tuple([]),
|
||||
});
|
||||
|
||||
export type LegacyPushServiceDeliveryWire = z.infer<typeof LegacyPushServiceDeliveryWire>;
|
||||
|
||||
export function toLegacyPushServiceDeliveryWire(
|
||||
config: PushRelayConfig,
|
||||
configVersion: number,
|
||||
): LegacyPushServiceDeliveryWire {
|
||||
return {
|
||||
enabled: true,
|
||||
config_version: configVersion,
|
||||
rollout_basis_points: LEGACY_PUSH_SERVICE_DELIVERY_ROLLOUT_BASIS_POINTS,
|
||||
rollout_salt: LEGACY_PUSH_SERVICE_DELIVERY_SALT,
|
||||
included_user_ids: [],
|
||||
excluded_user_ids: [],
|
||||
relay_consent_accepted: config.relay_consent_accepted,
|
||||
relay_consent_accepted_at: config.relay_consent_accepted_at,
|
||||
relay_consent_accepted_by: config.relay_consent_accepted_by,
|
||||
};
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {
|
||||
DEFAULT_PUSH_SERVICE_DELIVERY_CONFIG,
|
||||
type PushServiceDeliveryConfig,
|
||||
PushServiceDeliveryConfigSchema,
|
||||
PushServiceDeliveryConfigUpdateRequest,
|
||||
pushServiceDeliveryEnrols,
|
||||
} from '@fluxer/schema/src/domains/admin/PushServiceDeliverySchemas';
|
||||
import {describe, expect, test} from 'vitest';
|
||||
|
||||
const ADMIN_USER_ID = '1500000000000000001';
|
||||
const TARGETED_USER_ID = '1500000000000000002';
|
||||
|
||||
function createConfig(overrides: Partial<PushServiceDeliveryConfig> = {}): PushServiceDeliveryConfig {
|
||||
return {
|
||||
...DEFAULT_PUSH_SERVICE_DELIVERY_CONFIG,
|
||||
included_user_ids: [],
|
||||
excluded_user_ids: [],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe('push service delivery relay consent', () => {
|
||||
test('a stored configuration that predates relay consent reads back as not accepted', () => {
|
||||
expect(
|
||||
PushServiceDeliveryConfigSchema.parse({
|
||||
enabled: true,
|
||||
config_version: 4,
|
||||
rollout_basis_points: 10000,
|
||||
rollout_salt: 'push-service-delivery-v1',
|
||||
included_user_ids: [],
|
||||
excluded_user_ids: [],
|
||||
}),
|
||||
).toMatchObject({
|
||||
relay_consent_accepted: false,
|
||||
relay_consent_accepted_at: null,
|
||||
relay_consent_accepted_by: null,
|
||||
});
|
||||
});
|
||||
|
||||
test('the defaults export carries the unaccepted consent', () => {
|
||||
expect(DEFAULT_PUSH_SERVICE_DELIVERY_CONFIG.relay_consent_accepted).toBe(false);
|
||||
expect(DEFAULT_PUSH_SERVICE_DELIVERY_CONFIG.relay_consent_accepted_at).toBeNull();
|
||||
expect(DEFAULT_PUSH_SERVICE_DELIVERY_CONFIG.relay_consent_accepted_by).toBeNull();
|
||||
});
|
||||
|
||||
test('an accepted consent round-trips through the stored schema', () => {
|
||||
const accepted = {
|
||||
...DEFAULT_PUSH_SERVICE_DELIVERY_CONFIG,
|
||||
relay_consent_accepted: true,
|
||||
relay_consent_accepted_at: '2026-09-27T10:11:12.000Z',
|
||||
relay_consent_accepted_by: ADMIN_USER_ID,
|
||||
};
|
||||
expect(PushServiceDeliveryConfigSchema.parse(accepted)).toEqual(accepted);
|
||||
});
|
||||
|
||||
test('the update request takes the consent flag on its own', () => {
|
||||
expect(PushServiceDeliveryConfigUpdateRequest.parse({relay_consent_accepted: true})).toEqual({
|
||||
relay_consent_accepted: true,
|
||||
});
|
||||
});
|
||||
|
||||
test('the update request refuses a client-supplied acceptance stamp', () => {
|
||||
expect(
|
||||
PushServiceDeliveryConfigUpdateRequest.parse({
|
||||
relay_consent_accepted: true,
|
||||
relay_consent_accepted_at: '2020-01-01T00:00:00.000Z',
|
||||
relay_consent_accepted_by: ADMIN_USER_ID,
|
||||
}),
|
||||
).toEqual({relay_consent_accepted: true});
|
||||
});
|
||||
|
||||
test.each([
|
||||
{relay_consent_accepted_at: 'yesterday'},
|
||||
{relay_consent_accepted_at: '2026-09-27'},
|
||||
{relay_consent_accepted_by: 'not-an-id'},
|
||||
{relay_consent_accepted: 'yes'},
|
||||
])('rejects a malformed stored consent: %j', (value) => {
|
||||
expect(PushServiceDeliveryConfigSchema.safeParse(value).success).toBe(false);
|
||||
});
|
||||
|
||||
test('consent alone enrols nobody and refusing it excludes nobody', () => {
|
||||
const withConsent = createConfig({enabled: false, relay_consent_accepted: true});
|
||||
const withoutConsent = createConfig({enabled: true, rollout_basis_points: 10000});
|
||||
expect(pushServiceDeliveryEnrols(withConsent, TARGETED_USER_ID)).toBe(false);
|
||||
expect(pushServiceDeliveryEnrols(withoutConsent, TARGETED_USER_ID)).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -1,63 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {EXPERIMENT_BUCKET_RESOLUTION, experimentBucket} from '@fluxer/schema/src/domains/experiment/ExperimentBucket';
|
||||
import {z} from 'zod';
|
||||
|
||||
const PUSH_SERVICE_DELIVERY_ROLLOUT_BASIS_POINTS_MAX = EXPERIMENT_BUCKET_RESOLUTION;
|
||||
const PUSH_SERVICE_DELIVERY_MAX_TARGETED_USERS = 1000;
|
||||
const DEFAULT_PUSH_SERVICE_DELIVERY_SALT = 'push-service-delivery-v1';
|
||||
|
||||
const PUSH_SERVICE_DELIVERY_SALT_PATTERN = /^[\x20-\x7e]+$/u;
|
||||
|
||||
const PushServiceDeliveryTargetIdSchema = z.string().regex(/^\d{1,20}$/u);
|
||||
const PushServiceDeliveryTargetedUserIdsSchema = z
|
||||
.array(PushServiceDeliveryTargetIdSchema)
|
||||
.max(PUSH_SERVICE_DELIVERY_MAX_TARGETED_USERS);
|
||||
|
||||
const pushServiceDeliveryConfigFields = {
|
||||
enabled: z.boolean(),
|
||||
config_version: z.number().int().min(0),
|
||||
rollout_basis_points: z.number().int().min(0).max(PUSH_SERVICE_DELIVERY_ROLLOUT_BASIS_POINTS_MAX),
|
||||
rollout_salt: z.string().trim().min(1).max(64).regex(PUSH_SERVICE_DELIVERY_SALT_PATTERN),
|
||||
included_user_ids: PushServiceDeliveryTargetedUserIdsSchema,
|
||||
excluded_user_ids: PushServiceDeliveryTargetedUserIdsSchema,
|
||||
relay_consent_accepted: z.boolean(),
|
||||
relay_consent_accepted_at: z.iso.datetime().nullable(),
|
||||
relay_consent_accepted_by: PushServiceDeliveryTargetIdSchema.nullable(),
|
||||
};
|
||||
|
||||
export const PushServiceDeliveryConfigSchema = z.object({
|
||||
enabled: pushServiceDeliveryConfigFields.enabled.default(false),
|
||||
config_version: pushServiceDeliveryConfigFields.config_version.default(0),
|
||||
rollout_basis_points: pushServiceDeliveryConfigFields.rollout_basis_points.default(0),
|
||||
rollout_salt: pushServiceDeliveryConfigFields.rollout_salt.default(DEFAULT_PUSH_SERVICE_DELIVERY_SALT),
|
||||
included_user_ids: pushServiceDeliveryConfigFields.included_user_ids.default([]),
|
||||
excluded_user_ids: pushServiceDeliveryConfigFields.excluded_user_ids.default([]),
|
||||
relay_consent_accepted: pushServiceDeliveryConfigFields.relay_consent_accepted.default(false),
|
||||
relay_consent_accepted_at: pushServiceDeliveryConfigFields.relay_consent_accepted_at.default(null),
|
||||
relay_consent_accepted_by: pushServiceDeliveryConfigFields.relay_consent_accepted_by.default(null),
|
||||
});
|
||||
|
||||
export type PushServiceDeliveryConfig = z.infer<typeof PushServiceDeliveryConfigSchema>;
|
||||
|
||||
export const DEFAULT_PUSH_SERVICE_DELIVERY_CONFIG: PushServiceDeliveryConfig = PushServiceDeliveryConfigSchema.parse(
|
||||
{},
|
||||
);
|
||||
|
||||
export const PushServiceDeliveryConfigUpdateRequest = z
|
||||
.object(pushServiceDeliveryConfigFields)
|
||||
.omit({config_version: true, relay_consent_accepted_at: true, relay_consent_accepted_by: true})
|
||||
.partial();
|
||||
|
||||
export type PushServiceDeliveryConfigUpdateRequest = z.infer<typeof PushServiceDeliveryConfigUpdateRequest>;
|
||||
|
||||
export const PushServiceDeliveryConfigResponse = PushServiceDeliveryConfigSchema;
|
||||
|
||||
export type PushServiceDeliveryConfigResponse = z.infer<typeof PushServiceDeliveryConfigResponse>;
|
||||
|
||||
export function pushServiceDeliveryEnrols(config: PushServiceDeliveryConfig, userId: string): boolean {
|
||||
if (!config.enabled) return false;
|
||||
if (config.excluded_user_ids.includes(userId)) return false;
|
||||
if (config.included_user_ids.includes(userId)) return true;
|
||||
return experimentBucket(userId, config.rollout_salt) < config.rollout_basis_points;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import {RTC_REGION_ID_MAX_LENGTH, RTC_REGION_ID_MIN_LENGTH} from '@fluxer/constants/src/LimitConstants';
|
||||
import {GatewayRolloutConfigResponse} from '@fluxer/schema/src/domains/admin/GatewayRolloutSchemas';
|
||||
import {PushServiceDeliveryConfigResponse} from '@fluxer/schema/src/domains/admin/PushServiceDeliverySchemas';
|
||||
import {LegacyPushServiceDeliveryWire} from '@fluxer/schema/src/domains/admin/PushRelaySchemas';
|
||||
import {WebAuthnCredentialResponse} from '@fluxer/schema/src/domains/auth/AuthSchemas';
|
||||
import {ChannelResponse, RtcRegionResponse} from '@fluxer/schema/src/domains/channel/ChannelSchemas';
|
||||
import {VoiceStateResponse} from '@fluxer/schema/src/domains/gateway/GatewaySchemas';
|
||||
@@ -529,7 +529,7 @@ export const RpcResponse = z.discriminatedUnion('type', [
|
||||
.describe('Response type for push service delivery configuration'),
|
||||
data: z
|
||||
.object({
|
||||
config: PushServiceDeliveryConfigResponse.describe('Push service delivery configuration'),
|
||||
config: LegacyPushServiceDeliveryWire.describe('Push service delivery configuration'),
|
||||
})
|
||||
.describe('Push service delivery config result'),
|
||||
}),
|
||||
|
||||
Reference in new issue
Block a user