Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update validator to handle typebox literal values #154

Merged
merged 5 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/control/__tests__/createIndex.validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ describe('createIndex argument validations', () => {

expect(toThrow).rejects.toThrowError(PineconeArgumentError);
expect(toThrow).rejects.toThrowError(
'The argument to createIndex accepts multiple types. Either 1) 2) 3)'
"The argument to createIndex had type errors: property 'cloud' must be equal to one of: 'gcp', 'aws', 'azure'."
);
});

Expand All @@ -196,7 +196,7 @@ describe('createIndex argument validations', () => {

expect(toThrow).rejects.toThrowError(PineconeArgumentError);
expect(toThrow).rejects.toThrowError(
'The argument to createIndex accepts multiple types. Either 1) 2) 3)'
"The argument to createIndex had type errors: property 'cloud' must be equal to one of: 'gcp', 'aws', 'azure'."
);
});

Expand Down
3 changes: 1 addition & 2 deletions src/control/listIndexes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { IndexOperationsApi } from '../pinecone-generated-ts-fetch';
import type { IndexListMeta } from '../pinecone-generated-ts-fetch';

/**
* A description of indexes in your project.
Expand Down Expand Up @@ -27,7 +26,7 @@ export type IndexListDescription = {
export type IndexList = Array<IndexListDescription>;

export const listIndexes = (api: IndexOperationsApi) => {
return async (): Promise<Array<IndexListMeta>> => {
return async (): Promise<IndexList> => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Snuck this update in after merging the last PR.

const response = await api.listIndexes();

return response.databases || [];
Expand Down
52 changes: 45 additions & 7 deletions src/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,46 @@ const typeErrors = (
messageParts: Array<string>
) => {
const typeErrorsList: Array<string> = [];
const anyOfConstPropErrors: Array<ErrorObject> = errors.filter(
(error) =>
error.schemaPath.indexOf('anyOf') > -1 &&
error.keyword === 'const' &&
error.instancePath.length > 0
);
let errorCount = 0;

// handle possible literal types first
const propErrorGroups: { [key: string]: Array<ErrorObject> } = {};
if (anyOfConstPropErrors.length > 0) {
for (const error of anyOfConstPropErrors) {
const constValue = error.instancePath.slice(1);

if (propErrorGroups[constValue]) {
propErrorGroups[constValue].push(error);
} else {
propErrorGroups[constValue] = [error];
}
}
const properties = Object.keys(propErrorGroups);

properties.forEach((property) => {
const constValueErrors = propErrorGroups[property];

typeErrorsList.push(
`property '${property}' must be equal to one of: ` +
Object.values(constValueErrors)
.map((group) => `'${group.params.allowedValue}'`)
.join(', ')
);
});
}

// typebox also emits type errors for each value of a literal so we want to exclude these
const anyOfKeys = Object.keys(propErrorGroups);
for (let i = 0; i < errors.length; i++) {
const e = errors[i];

if (e.keyword === 'type') {
if (e.keyword === 'type' && !anyOfKeys.includes(e.instancePath.slice(1))) {
errorCount += 1;
if (errorCount <= maxErrors) {
formatIndividualError(e, typeErrorsList);
Expand Down Expand Up @@ -181,13 +215,19 @@ const validationErrors = (
};

export const errorFormatter = (subject: string, errors: Array<ErrorObject>) => {
const anyOfErrors = errors.filter(
const messageParts: Array<string> = [];

const anyOfArgumentErrors = errors.filter(
(error) =>
error.schemaPath.indexOf('anyOf') > -1 && error.keyword !== 'anyOf'
error.schemaPath.indexOf('anyOf') > -1 &&
error.keyword !== 'anyOf' &&
error.keyword !== 'const' &&
error.keyword !== 'type'
);
if (anyOfErrors.length > 0) {

if (anyOfArgumentErrors.length > 0) {
const groups = {};
for (const error of anyOfErrors) {
for (const error of anyOfArgumentErrors) {
const schemaPathMatch = schemaPathGroupNumberRegex.exec(error.schemaPath);
const groupNumber = schemaPathMatch ? schemaPathMatch[1] : 'unknown';
// Remove the anyOf portion of the schema path to avoid infinite loop
Expand All @@ -214,8 +254,6 @@ export const errorFormatter = (subject: string, errors: Array<ErrorObject>) => {
);
}

const messageParts: Array<string> = [];

neverErrors(subject, errors, messageParts);
missingPropertiesErrors(subject, errors, messageParts);
typeErrors(subject, errors, messageParts);
Expand Down
Loading