Skip to content

Commit

Permalink
Merge pull request #145 from mapado/mapping-name-error-dx
Browse files Browse the repository at this point in the history
  • Loading branch information
jdeniau authored Aug 29, 2024
2 parents 942d5ff + e8b21fb commit 503c1b2
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 2 deletions.
36 changes: 35 additions & 1 deletion __tests__/RestClientSdk.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,17 @@ import tokenStorage from '../__mocks__/tokenStorage';

const mapping = new Mapping('/v2');
const testMetadata = new ClassMetadata('test');
const cartMetadata = new ClassMetadata('cart');
const cartItemMetadata = new ClassMetadata('cartItem');
const orderMetadata = new ClassMetadata('order');

testMetadata.setAttributeList([new Attribute('@id', '@id', 'string', true)]);
mapping.setMapping([testMetadata]);
mapping.setMapping([
testMetadata,
cartMetadata,
cartItemMetadata,
orderMetadata,
]);

describe('Mapado Sdk tests', () => {
test('Test wrong SDK configuration', () => {
Expand Down Expand Up @@ -66,4 +75,29 @@ describe('Mapado Sdk tests', () => {
expect(sdk.getRepository('test').getPathBase()).toBe('/test');
expect(sdk.getRepository('test').sdk).toBe(sdk);
});

test('Mapping not found', () => {
const sdk = new RestClientSdk(
tokenStorage,
{ path: 'my.api.com', scheme: 'https' },
mapping
);

expect(() => sdk.getRepository('toast')).toThrowError(
'Unable to get metadata for repository "toast". Did you mean "test"?'
);

expect(() => sdk.getRepository('Cart')).toThrowError(
'Unable to get metadata for repository "Cart". Did you mean "cart"?'
);

expect(() => sdk.getRepository('cart_items')).toThrowError(
'Unable to get metadata for repository "cart_items". Did you mean "cartItem"?'
);

// word is too far from any other words
expect(() => sdk.getRepository('zargiblou')).toThrowError(
'Unable to get metadata for repository "zargiblou".'
);
});
});
9 changes: 8 additions & 1 deletion src/RestClientSdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import UnitOfWork from './UnitOfWork';
import AbstractClient from './client/AbstractClient';
import JsSerializer from './serializer/JsSerializer';
import SerializerInterface from './serializer/SerializerInterface';
import { findClosestWord } from './utils/levenshtein';
import { Logger } from './utils/logging';
import { generateRepository } from './utils/repositoryGenerator';

Expand Down Expand Up @@ -80,7 +81,13 @@ class RestClientSdk<M extends SdkMetadata>
const metadata = this.mapping.getClassMetadataByKey(key);

if (!metadata) {
throw new Error(`Unable to get metadata for repository ${key}`);
const closeKey = findClosestWord(key, this.mapping.getMappingKeys(), 5);

const suggestions = closeKey ? ` Did you mean "${closeKey}"?` : '';

throw new Error(
`Unable to get metadata for repository "${key}".${suggestions}`
);
}

// eslint-disable-next-line new-cap
Expand Down
53 changes: 53 additions & 0 deletions src/utils/levenshtein.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
function levenshteinDistance(a: string, b: string): number {
const matrix: number[][] = [];

// Increment along the first column of each row
for (let i = 0; i <= b.length; i += 1) {
matrix[i] = [i];
}

// Increment each column in the first row
for (let j = 0; j <= a.length; j += 1) {
matrix[0][j] = j;
}

// Fill in the rest of the matrix
for (let i = 1; i <= b.length; i += 1) {
for (let j = 1; j <= a.length; j += 1) {
if (b.charAt(i - 1) === a.charAt(j - 1)) {
matrix[i][j] = matrix[i - 1][j - 1];
} else {
matrix[i][j] = Math.min(
matrix[i - 1][j - 1] + 1, // substitution
matrix[i][j - 1] + 1, // insertion
matrix[i - 1][j] + 1 // deletion
);
}
}
}

return matrix[b.length][a.length];
}

// eslint-disable-next-line import/prefer-default-export
export function findClosestWord(
word: string,
list: string[],
maxDistance = Infinity
): string | null {
let closestWord: string | null = null;
let minDistance = Infinity;

// eslint-disable-next-line no-plusplus
for (let i = 0; i < list.length; i += 1) {
const candidate = list[i];
const distance = levenshteinDistance(word, candidate);

if (distance < minDistance && distance <= maxDistance) {
minDistance = distance;
closestWord = candidate;
}
}

return closestWord;
}

0 comments on commit 503c1b2

Please sign in to comment.