Skip to content

Commit

Permalink
chore: Move stringUtils to @studio/pure-functions (#13918)
Browse files Browse the repository at this point in the history
  • Loading branch information
wrt95 authored Oct 25, 2024
1 parent 2d3659d commit fb7b75b
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 113 deletions.
5 changes: 2 additions & 3 deletions frontend/app-development/utils/metadataUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import type {
DataModelMetadataJson,
DataModelMetadataXsd,
} from 'app-shared/types/DataModelMetadata';
import { replaceEnd } from 'app-shared/utils/stringUtils';
import { ArrayUtils } from '@studio/pure-functions';
import { ArrayUtils, StringUtils } from '@studio/pure-functions';
import type { MetadataOption } from '../types/MetadataOption';
import type { MetadataOptionsGroup } from '../types/MetadataOptionsGroup';
import { removeSchemaExtension } from 'app-shared/utils/filenameUtils';
Expand All @@ -23,7 +22,7 @@ export const filterOutXsdDataIfJsonDataExist = (
({ fileName }) =>
!jsonData.find(
({ fileName: jsonFileName }) =>
jsonFileName === replaceEnd(fileName, '.xsd', '.schema.json'),
jsonFileName === StringUtils.replaceEnd(fileName, '.xsd', '.schema.json'),
),
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,56 @@ describe('StringUtils', () => {
expect(input).toBe('abc/def/ghi');
});
});

describe('replaceEnd', () => {
it('Replaces the given substring with the given replacement at the end of the string', () => {
expect(StringUtils.replaceEnd('abc/def/ghi', 'ghi', 'xyz')).toBe('abc/def/xyz');
});

it('Does not replace the given substring other places than at the end', () => {
expect(StringUtils.replaceEnd('abcdefghi', 'abc', 'xyz')).toBe('abcdefghi');
expect(StringUtils.replaceEnd('abcdefghi', 'def', 'xyz')).toBe('abcdefghi');
expect(StringUtils.replaceEnd('abcdefghidef', 'def', 'xyz')).toBe('abcdefghixyz');
});
});

describe('replaceStart', () => {
it('Replaces the given substring with the given replacement at the start of the string', () => {
expect(StringUtils.replaceStart('abc/def/ghi', 'abc', 'xyz')).toBe('xyz/def/ghi');
});

it('Does not replace the given substring other places than at the start', () => {
expect(StringUtils.replaceStart('abcdefghi', 'ghi', 'xyz')).toBe('abcdefghi');
expect(StringUtils.replaceStart('abcdefghi', 'def', 'xyz')).toBe('abcdefghi');
expect(StringUtils.replaceStart('defabcdefghi', 'def', 'xyz')).toBe('xyzabcdefghi');
});
});

describe('substringBeforeLast', () => {
it('Returns substring before last occurrence of separator', () => {
expect(StringUtils.substringBeforeLast('abc/def/ghi', '/')).toBe('abc/def');
});

it('Returns whole string if separator is not found', () => {
expect(StringUtils.substringBeforeLast('abc', '/')).toBe('abc');
});

it('Returns whole string if there are no characters before the last separator', () => {
expect(StringUtils.substringBeforeLast('/abc', '/')).toBe('');
});
});

describe('substringAfterLast', () => {
it('Returns substring after last occurrence of separator', () => {
expect(StringUtils.substringAfterLast('abc/def/ghi', '/')).toBe('ghi');
});

it('Returns whole string if separator is not found', () => {
expect(StringUtils.substringAfterLast('abc', '/')).toBe('abc');
});

it('Returns empty string if there are no characters after the last separator', () => {
expect(StringUtils.substringAfterLast('abc/def/', '/')).toBe('');
});
});
});
46 changes: 46 additions & 0 deletions frontend/libs/studio-pure-functions/src/StringUtils/StringUtils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ArrayUtils } from '../ArrayUtils';

export class StringUtils {
/**
* Removes any of the given substrings from the start of the string.
Expand Down Expand Up @@ -32,4 +34,48 @@ export class StringUtils {
}
return str;
};

/**
* Replaces the given substring with the given replacement at the end of the string.
* If the substring does not appear at the end of the string, the string is returned unchanged.
* @param str The string to search in.
* @param substring The substring to search for.
* @param replacement The replacement to replace the substring with.
* @returns The string with the substring replaced at the end.
*/
static replaceEnd = (str: string, substring: string, replacement: string): string =>
str.replace(new RegExp(substring + '$'), replacement);

/**
* Replaces the given substring with the given replacement at the start of the string.
* If the substring does not appear at the start of the string, the string is returned unchanged.
* @param str The string to search in.
* @param substring The substring to search for.
* @param replacement The replacement to replace the substring with.
* @returns The string with the substring replaced at the start.
*/
static replaceStart = (str: string, substring: string, replacement: string): string => {
if (str.startsWith(substring)) {
return replacement + str.slice(substring.length);
}
return str;
};

/**
* Returns substring before last occurrence of separator.
* @param str The string to search in.
* @param separator The separator to search for.
* @returns The substring before the last occurrence of the given separator.
*/
static substringBeforeLast = (str: string, separator: string): string =>
str.includes(separator) ? str.substring(0, str.lastIndexOf(separator)) : str;

/**
* Returns substring after last occurrence of separator.
* @param str The string to search in.
* @param separator The separator to search for.
* @returns The substring after the last occurrence of the given separator.
*/
static substringAfterLast = (str: string, separator: string): string =>
ArrayUtils.last(str.split(separator)) || '';
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
import { ROOT_POINTER, UNIQUE_POINTER_PREFIX } from '../constants';
import type { ReferenceNode } from '../../types/ReferenceNode';
import { ObjectUtils, ArrayUtils, StringUtils } from '@studio/pure-functions';
import { replaceStart } from 'app-shared/utils/stringUtils';
import {
createDefinitionPointer,
createPropertyPointer,
Expand Down Expand Up @@ -369,7 +368,7 @@ export class SchemaModel extends SchemaModelBase {
const node = this.getNodeBySchemaPointer(newPointer); // Expects the node map to be updated
if (isFieldOrCombination(node) && node.children) {
const makeNewPointer = (schemaPointer: string) =>
replaceStart(schemaPointer, oldPointer, newPointer);
StringUtils.replaceStart(schemaPointer, oldPointer, newPointer);
node.children.forEach((childPointer) => {
const newPointer = makeNewPointer(childPointer);
this.changePointer(childPointer, newPointer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ import { expect } from '@jest/globals';
import { CombinationKind, FieldType, Keyword, ObjectKind, StrRestrictionKey } from '../../types';
import { ROOT_POINTER } from '../constants';
import { getPointers } from '../mappers/getPointers';
import { substringAfterLast, substringBeforeLast } from 'app-shared/utils/stringUtils';
import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs';
import { validateTestUiSchema } from '../../../test/validateTestUiSchema';
import { SchemaModel } from '../SchemaModel';
import type { FieldNode } from '../../types/FieldNode';
import type { ReferenceNode } from '../../types/ReferenceNode';
import type { CombinationNode } from '../../types/CombinationNode';
import { StringUtils } from '@studio/pure-functions';

describe('ui-schema-reducers', () => {
let result: SchemaModel;
Expand All @@ -73,7 +73,7 @@ describe('ui-schema-reducers', () => {
it('Converts a property to a root level definition', () => {
const { schemaPointer } = stringNodeMock;
result = promoteProperty(createNewModelMock(), schemaPointer);
const expectedPointer = `${ROOT_POINTER}/$defs/${substringAfterLast(schemaPointer, '/')}`;
const expectedPointer = `${ROOT_POINTER}/$defs/${StringUtils.substringAfterLast(schemaPointer, '/')}`;
expect(getPointers(result.asArray())).toContain(expectedPointer);
expect(result.getNodeBySchemaPointer(expectedPointer)).toMatchObject({
fieldType: stringNodeMock.fieldType,
Expand Down Expand Up @@ -223,7 +223,7 @@ describe('ui-schema-reducers', () => {
const name = 'new name';
const callback = jest.fn();
const args: SetPropertyNameArgs = { path: schemaPointer, name, callback };
const expectedPointer = substringBeforeLast(schemaPointer, '/') + '/' + name;
const expectedPointer = StringUtils.substringBeforeLast(schemaPointer, '/') + '/' + name;

it('Sets the name of the given property', () => {
result = setPropertyName(createNewModelMock(), args);
Expand Down
60 changes: 0 additions & 60 deletions frontend/packages/shared/src/utils/stringUtils.test.ts

This file was deleted.

45 changes: 0 additions & 45 deletions frontend/packages/shared/src/utils/stringUtils.ts

This file was deleted.

0 comments on commit fb7b75b

Please sign in to comment.