From 59115c8dc5a5107d6d55e5860b60e8d553d347ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 27 Oct 2023 17:48:21 +0200 Subject: [PATCH] Remove instance normalizer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- src/api/hooks/groups/useGroups.test.ts | 4 +- src/api/hooks/groups/usePendingGroups.test.ts | 4 +- .../auth-login/components/login-form.test.tsx | 6 +- .../auth-login/components/login-page.test.tsx | 4 +- .../discover/search/search.test.tsx | 4 +- src/features/groups/discover.test.tsx | 4 +- src/hooks/useGroupsPath.test.ts | 6 +- src/jest/mock-stores.tsx | 6 +- src/normalizers/index.ts | 1 - src/normalizers/instance.test.ts | 214 ------------------ src/normalizers/instance.ts | 164 -------------- src/types/entities.ts | 3 - 12 files changed, 19 insertions(+), 401 deletions(-) delete mode 100644 src/normalizers/instance.test.ts delete mode 100644 src/normalizers/instance.ts diff --git a/src/api/hooks/groups/useGroups.test.ts b/src/api/hooks/groups/useGroups.test.ts index 950c4eb81..65a699969 100644 --- a/src/api/hooks/groups/useGroups.test.ts +++ b/src/api/hooks/groups/useGroups.test.ts @@ -1,13 +1,13 @@ import { __stub } from 'soapbox/api'; import { buildGroup } from 'soapbox/jest/factory'; import { renderHook, waitFor } from 'soapbox/jest/test-helpers'; -import { normalizeInstance } from 'soapbox/normalizers'; +import { instanceSchema } from 'soapbox/schemas'; import { useGroups } from './useGroups'; const group = buildGroup({ id: '1', display_name: 'soapbox' }); const store = { - instance: normalizeInstance({ + instance: instanceSchema.parse({ version: '3.4.1 (compatible; TruthSocial 1.0.0+unreleased)', }), }; diff --git a/src/api/hooks/groups/usePendingGroups.test.ts b/src/api/hooks/groups/usePendingGroups.test.ts index f2f76178c..33190ae0b 100644 --- a/src/api/hooks/groups/usePendingGroups.test.ts +++ b/src/api/hooks/groups/usePendingGroups.test.ts @@ -2,14 +2,14 @@ import { __stub } from 'soapbox/api'; import { Entities } from 'soapbox/entity-store/entities'; import { buildAccount, buildGroup } from 'soapbox/jest/factory'; import { renderHook, waitFor } from 'soapbox/jest/test-helpers'; -import { normalizeInstance } from 'soapbox/normalizers'; +import { instanceSchema } from 'soapbox/schemas'; import { usePendingGroups } from './usePendingGroups'; const id = '1'; const group = buildGroup({ id, display_name: 'soapbox' }); const store = { - instance: normalizeInstance({ + instance: instanceSchema.parse({ version: '3.4.1 (compatible; TruthSocial 1.0.0+unreleased)', }), me: '1', diff --git a/src/features/auth-login/components/login-form.test.tsx b/src/features/auth-login/components/login-form.test.tsx index cb14335c2..2cecaa059 100644 --- a/src/features/auth-login/components/login-form.test.tsx +++ b/src/features/auth-login/components/login-form.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { fireEvent, render, screen } from 'soapbox/jest/test-helpers'; -import { normalizeInstance } from 'soapbox/normalizers'; +import { instanceSchema } from 'soapbox/schemas'; import LoginForm from './login-form'; @@ -9,7 +9,7 @@ describe('', () => { it('renders for Pleroma', () => { const mockFn = vi.fn(); const store = { - instance: normalizeInstance({ + instance: instanceSchema.parse({ version: '2.7.2 (compatible; Pleroma 2.3.0)', }), }; @@ -22,7 +22,7 @@ describe('', () => { it('renders for Mastodon', () => { const mockFn = vi.fn(); const store = { - instance: normalizeInstance({ + instance: instanceSchema.parse({ version: '3.0.0', }), }; diff --git a/src/features/auth-login/components/login-page.test.tsx b/src/features/auth-login/components/login-page.test.tsx index 58538bebf..fc308ff4f 100644 --- a/src/features/auth-login/components/login-page.test.tsx +++ b/src/features/auth-login/components/login-page.test.tsx @@ -1,14 +1,14 @@ import React from 'react'; import { render, screen } from 'soapbox/jest/test-helpers'; -import { normalizeInstance } from 'soapbox/normalizers'; +import { instanceSchema } from 'soapbox/schemas'; import LoginPage from './login-page'; describe('', () => { it('renders correctly on load', () => { const store = { - instance: normalizeInstance({ + instance: instanceSchema.parse({ version: '2.7.2 (compatible; Pleroma 2.3.0)', }), }; diff --git a/src/features/groups/components/discover/search/search.test.tsx b/src/features/groups/components/discover/search/search.test.tsx index f7dbafa3d..5c25cc5a3 100644 --- a/src/features/groups/components/discover/search/search.test.tsx +++ b/src/features/groups/components/discover/search/search.test.tsx @@ -3,12 +3,12 @@ import React from 'react'; import { __stub } from 'soapbox/api'; import { buildGroup } from 'soapbox/jest/factory'; import { render, screen, waitFor } from 'soapbox/jest/test-helpers'; -import { normalizeInstance } from 'soapbox/normalizers'; +import { instanceSchema } from 'soapbox/schemas'; import Search from './search'; const store = { - instance: normalizeInstance({ + instance: instanceSchema.parse({ version: '3.4.1 (compatible; TruthSocial 1.0.0+unreleased)', }), }; diff --git a/src/features/groups/discover.test.tsx b/src/features/groups/discover.test.tsx index 1f14f1022..880b17c1c 100644 --- a/src/features/groups/discover.test.tsx +++ b/src/features/groups/discover.test.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { buildAccount } from 'soapbox/jest/factory'; import { render, screen, waitFor } from 'soapbox/jest/test-helpers'; -import { normalizeInstance } from 'soapbox/normalizers'; +import { instanceSchema } from 'soapbox/schemas'; import Discover from './discover'; @@ -32,7 +32,7 @@ const store: any = { }, }), }, - instance: normalizeInstance({ + instance: instanceSchema.parse({ version: '3.4.1 (compatible; TruthSocial 1.0.0)', software: 'TRUTHSOCIAL', }), diff --git a/src/hooks/useGroupsPath.test.ts b/src/hooks/useGroupsPath.test.ts index a7ba8979b..72af53731 100644 --- a/src/hooks/useGroupsPath.test.ts +++ b/src/hooks/useGroupsPath.test.ts @@ -1,14 +1,14 @@ import { __stub } from 'soapbox/api'; import { buildAccount, buildGroup, buildGroupRelationship } from 'soapbox/jest/factory'; import { renderHook, waitFor } from 'soapbox/jest/test-helpers'; -import { normalizeInstance } from 'soapbox/normalizers'; +import { instanceSchema } from 'soapbox/schemas'; import { useGroupsPath } from './useGroupsPath'; describe('useGroupsPath()', () => { test('without the groupsDiscovery feature', () => { const store = { - instance: normalizeInstance({ + instance: instanceSchema.parse({ version: '2.7.2 (compatible; Pleroma 2.3.0)', }), }; @@ -24,7 +24,7 @@ describe('useGroupsPath()', () => { beforeEach(() => { const userId = '1'; store = { - instance: normalizeInstance({ + instance: instanceSchema.parse({ version: '3.4.1 (compatible; TruthSocial 1.0.0+unreleased)', }), me: userId, diff --git a/src/jest/mock-stores.tsx b/src/jest/mock-stores.tsx index 0dc4bae79..c3ba64aac 100644 --- a/src/jest/mock-stores.tsx +++ b/src/jest/mock-stores.tsx @@ -1,13 +1,13 @@ import alexJson from 'soapbox/__fixtures__/pleroma-account.json'; -import { normalizeInstance } from 'soapbox/normalizers'; +import { instanceSchema } from 'soapbox/schemas'; import { buildAccount } from './factory'; /** Store with registrations open. */ -const storeOpen = { instance: normalizeInstance({ registrations: true }) }; +const storeOpen = { instance: instanceSchema.parse({ registrations: true }) }; /** Store with registrations closed. */ -const storeClosed = { instance: normalizeInstance({ registrations: false }) }; +const storeClosed = { instance: instanceSchema.parse({ registrations: false }) }; /** Store with a logged-in user. */ const storeLoggedIn = { diff --git a/src/normalizers/index.ts b/src/normalizers/index.ts index 12bb77d0c..03fb83021 100644 --- a/src/normalizers/index.ts +++ b/src/normalizers/index.ts @@ -13,7 +13,6 @@ export { FilterStatusRecord, normalizeFilterStatus } from './filter-status'; export { normalizeGroup } from './group'; export { GroupRelationshipRecord, normalizeGroupRelationship } from './group-relationship'; export { HistoryRecord, normalizeHistory } from './history'; -export { InstanceRecord, normalizeInstance } from './instance'; export { ListRecord, normalizeList } from './list'; export { LocationRecord, normalizeLocation } from './location'; export { MentionRecord, normalizeMention } from './mention'; diff --git a/src/normalizers/instance.test.ts b/src/normalizers/instance.test.ts deleted file mode 100644 index 46793dfaf..000000000 --- a/src/normalizers/instance.test.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { Map as ImmutableMap, fromJS } from 'immutable'; - -import { normalizeInstance } from './instance'; - -describe('normalizeInstance()', () => { - it('normalizes an empty Map', () => { - const expected = { - approval_required: false, - contact_account: {}, - configuration: { - media_attachments: {}, - chats: { - max_characters: 5000, - max_media_attachments: 1, - }, - polls: { - max_options: 4, - max_characters_per_option: 25, - min_expiration: 300, - max_expiration: 2629746, - }, - statuses: { - max_characters: 500, - max_media_attachments: 4, - }, - groups: { - max_characters_name: 50, - max_characters_description: 160, - }, - }, - description: '', - description_limit: 1500, - email: '', - feature_quote: false, - fedibird_capabilities: [], - invites_enabled: false, - languages: [], - login_message: '', - pleroma: { - metadata: { - account_activation_required: false, - birthday_min_age: 0, - birthday_required: false, - features: [], - federation: { - enabled: true, - exclusions: false, - }, - }, - stats: {}, - }, - registrations: false, - rules: [], - short_description: '', - stats: { - domain_count: 0, - status_count: 0, - user_count: 0, - }, - title: '', - thumbnail: '', - uri: '', - urls: {}, - version: '0.0.0', - nostr: { - pubkey: undefined, - relay: undefined, - }, - }; - - const result = normalizeInstance(ImmutableMap()); - expect(result.toJS()).toEqual(expected); - }); - - it('normalizes Pleroma instance with Mastodon configuration format', async () => { - const instance = await import('soapbox/__fixtures__/pleroma-instance.json'); - - const expected = { - configuration: { - statuses: { - max_characters: 5000, - max_media_attachments: Infinity, - }, - polls: { - max_options: 20, - max_characters_per_option: 200, - min_expiration: 0, - max_expiration: 31536000, - }, - }, - }; - - const result = normalizeInstance(instance); - expect(result.toJS()).toMatchObject(expected); - }); - - it('normalizes Mastodon instance with retained configuration', async () => { - const instance = await import('soapbox/__fixtures__/mastodon-instance.json'); - - const expected = { - configuration: { - statuses: { - max_characters: 500, - max_media_attachments: 4, - characters_reserved_per_url: 23, - }, - media_attachments: { - image_size_limit: 10485760, - image_matrix_limit: 16777216, - video_size_limit: 41943040, - video_frame_rate_limit: 60, - video_matrix_limit: 2304000, - }, - polls: { - max_options: 4, - max_characters_per_option: 50, - min_expiration: 300, - max_expiration: 2629746, - }, - }, - }; - - const result = normalizeInstance(instance); - expect(result.toJS()).toMatchObject(expected); - }); - - it('normalizes Mastodon 3.0.0 instance with default configuration', async () => { - const instance = await import('soapbox/__fixtures__/mastodon-3.0.0-instance.json'); - - const expected = { - configuration: { - statuses: { - max_characters: 500, - max_media_attachments: 4, - }, - polls: { - max_options: 4, - max_characters_per_option: 25, - min_expiration: 300, - max_expiration: 2629746, - }, - }, - }; - - const result = normalizeInstance(instance); - expect(result.toJS()).toMatchObject(expected); - }); - - it('normalizes Fedibird instance', async () => { - const instance = await import('soapbox/__fixtures__/fedibird-instance.json'); - const result = normalizeInstance(instance); - - // Sets description_limit - expect(result.description_limit).toEqual(1500); - - // Preserves fedibird_capabilities - expect(result.fedibird_capabilities).toEqual(fromJS(instance.fedibird_capabilities)); - }); - - it('normalizes Mitra instance', async () => { - const instance = await import('soapbox/__fixtures__/mitra-instance.json'); - const result = normalizeInstance(instance); - - // Adds configuration and description_limit - expect(result.get('configuration') instanceof ImmutableMap).toBe(true); - expect(result.get('description_limit')).toBe(1500); - }); - - it('normalizes GoToSocial instance', async () => { - const instance = await import('soapbox/__fixtures__/gotosocial-instance.json'); - const result = normalizeInstance(instance); - - // Normalizes max_toot_chars - expect(result.getIn(['configuration', 'statuses', 'max_characters'])).toEqual(5000); - expect(result.has('max_toot_chars')).toBe(false); - - // Adds configuration and description_limit - expect(result.get('configuration') instanceof ImmutableMap).toBe(true); - expect(result.get('description_limit')).toBe(1500); - }); - - it('normalizes Friendica instance', async () => { - const instance = await import('soapbox/__fixtures__/friendica-instance.json'); - const result = normalizeInstance(instance); - - // Normalizes max_toot_chars - expect(result.getIn(['configuration', 'statuses', 'max_characters'])).toEqual(200000); - expect(result.has('max_toot_chars')).toBe(false); - - // Adds configuration and description_limit - expect(result.get('configuration') instanceof ImmutableMap).toBe(true); - expect(result.get('description_limit')).toBe(1500); - }); - - it('normalizes a Mastodon RC version', async () => { - const instance = await import('soapbox/__fixtures__/mastodon-instance-rc.json'); - const result = normalizeInstance(instance); - - expect(result.version).toEqual('3.5.0-rc1'); - }); - - it('normalizes Pixelfed instance', async () => { - const instance = await import('soapbox/__fixtures__/pixelfed-instance.json'); - const result = normalizeInstance(instance); - expect(result.title).toBe('pixelfed'); - }); - - it('renames Akkoma to Pleroma', async () => { - const instance = await import('soapbox/__fixtures__/akkoma-instance.json'); - const result = normalizeInstance(instance); - - expect(result.version).toEqual('2.7.2 (compatible; Pleroma 2.4.50+akkoma)'); - }); -}); diff --git a/src/normalizers/instance.ts b/src/normalizers/instance.ts deleted file mode 100644 index ea3327fb6..000000000 --- a/src/normalizers/instance.ts +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Instance normalizer: - * Converts API instances into our internal format. - * @see {@link https://docs.joinmastodon.org/entities/instance/} - */ -import { - Map as ImmutableMap, - List as ImmutableList, - Record as ImmutableRecord, - fromJS, -} from 'immutable'; - -import { parseVersion, PLEROMA } from 'soapbox/utils/features'; -import { mergeDefined } from 'soapbox/utils/normalizers'; -import { isNumber } from 'soapbox/utils/numbers'; - -// Use Mastodon defaults -// https://docs.joinmastodon.org/entities/instance/ -export const InstanceRecord = ImmutableRecord({ - approval_required: false, - contact_account: ImmutableMap(), - configuration: ImmutableMap({ - media_attachments: ImmutableMap(), - chats: ImmutableMap({ - max_characters: 5000, - max_media_attachments: 1, - }), - polls: ImmutableMap({ - max_options: 4, - max_characters_per_option: 25, - min_expiration: 300, - max_expiration: 2629746, - }), - statuses: ImmutableMap({ - max_characters: 500, - max_media_attachments: 4, - }), - groups: ImmutableMap({ - max_characters_name: 50, - max_characters_description: 160, - }), - }), - description: '', - description_limit: 1500, - email: '', - feature_quote: false, - fedibird_capabilities: ImmutableList(), - invites_enabled: false, - languages: ImmutableList(), - login_message: '', - pleroma: ImmutableMap({ - metadata: ImmutableMap({ - account_activation_required: false, - birthday_min_age: 0, - birthday_required: false, - features: ImmutableList(), - federation: ImmutableMap({ - enabled: true, - exclusions: false, - }), - }), - stats: ImmutableMap(), - }), - registrations: false, - rules: ImmutableList(), - short_description: '', - stats: ImmutableMap({ - domain_count: 0, - status_count: 0, - user_count: 0, - }), - nostr: ImmutableMap({ - relay: undefined as string | undefined, - pubkey: undefined as string | undefined, - }), - title: '', - thumbnail: '', - uri: '', - urls: ImmutableMap(), - version: '0.0.0', -}); - -// Build Mastodon configuration from Pleroma instance -const pleromaToMastodonConfig = (instance: ImmutableMap) => { - return ImmutableMap({ - statuses: ImmutableMap({ - max_characters: instance.get('max_toot_chars'), - }), - polls: ImmutableMap({ - max_options: instance.getIn(['poll_limits', 'max_options']), - max_characters_per_option: instance.getIn(['poll_limits', 'max_option_chars']), - min_expiration: instance.getIn(['poll_limits', 'min_expiration']), - max_expiration: instance.getIn(['poll_limits', 'max_expiration']), - }), - }); -}; - -// Get the software's default attachment limit -const getAttachmentLimit = (software: string | null) => software === PLEROMA ? Infinity : 4; - -// Normalize version -const normalizeVersion = (instance: ImmutableMap) => { - return instance.update('version', '0.0.0', version => { - // Handle Mastodon release candidates - if (new RegExp(/[0-9.]+rc[0-9]+/g).test(version)) { - return version.split('rc').join('-rc'); - } else { - return version; - } - }); -}; - -/** Rename Akkoma to Pleroma+akkoma */ -const fixAkkoma = (instance: ImmutableMap) => { - const version: string = instance.get('version', ''); - - if (version.includes('Akkoma')) { - return instance.set('version', '2.7.2 (compatible; Pleroma 2.4.50+akkoma)'); - } else { - return instance; - } -}; - -/** Set Takahē version to a Pleroma-like string */ -const fixTakahe = (instance: ImmutableMap) => { - const version: string = instance.get('version', ''); - - if (version.startsWith('takahe/')) { - return instance.set('version', `0.0.0 (compatible; Takahe ${version.slice(7)})`); - } else { - return instance; - } -}; - -// Normalize instance (Pleroma, Mastodon, etc.) to Mastodon's format -export const normalizeInstance = (instance: Record) => { - return InstanceRecord( - ImmutableMap(fromJS(instance)).withMutations((instance: ImmutableMap) => { - const { software } = parseVersion(instance.get('version')); - const mastodonConfig = pleromaToMastodonConfig(instance); - - // Merge configuration - instance.update('configuration', ImmutableMap(), configuration => ( - configuration.mergeDeepWith(mergeDefined, mastodonConfig) - )); - - // If max attachments isn't set, check the backend software - instance.updateIn(['configuration', 'statuses', 'max_media_attachments'], value => { - return isNumber(value) ? value : getAttachmentLimit(software); - }); - - // Urls can't be null, fix for Friendica - if (instance.get('urls') === null) instance.delete('urls'); - - // Normalize version - normalizeVersion(instance); - fixTakahe(instance); - fixAkkoma(instance); - - // Merge defaults - instance.mergeDeepWith(mergeDefined, InstanceRecord()); - }), - ); -}; diff --git a/src/types/entities.ts b/src/types/entities.ts index 20eb419af..98d36a07c 100644 --- a/src/types/entities.ts +++ b/src/types/entities.ts @@ -12,7 +12,6 @@ import { FilterKeywordRecord, FilterStatusRecord, HistoryRecord, - InstanceRecord, ListRecord, LocationRecord, MentionRecord, @@ -41,7 +40,6 @@ type Filter = ReturnType; type FilterKeyword = ReturnType; type FilterStatus = ReturnType; type History = ReturnType; -type Instance = ReturnType; type List = ReturnType; type Location = ReturnType; type Mention = ReturnType; @@ -77,7 +75,6 @@ export { FilterKeyword, FilterStatus, History, - Instance, List, Location, Mention,