Skip to content

Commit

Permalink
feat: GitHubProjectInvalidValueError (#141)
Browse files Browse the repository at this point in the history
closes #136
  • Loading branch information
gr2m committed Oct 5, 2023
1 parent e55dac2 commit c3afa6e
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 244 deletions.
165 changes: 164 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1243,10 +1243,160 @@ Example for `error.toHumanMessage()`:

> "NOPE" could not be matched with any of the existing field names: "My text", "My number", "My Date". If the field should be considered optional, then set it to "nope: { name: "NOPE", optional: true}
#### `GitHubProjectUnknownFieldOptionError`
#### `GitHubProjectInvalidValueError`

Thrown when attempting to set a single select project field to a value that is not included in the field's configured options.

```js
import Project, { GitHubProjectInvalidValueError } from "github-project";

try {
await myScript(new Project(options));
} catch (error) {
if (error instanceof GitHubProjectInvalidValueError) {
analytics.track("GitHubProjectInvalidValueError", {
fieldName: error.details.field.name,
userValue: error.details.userValue,
});

myLogger.error(
{
code: error.code,
details: error.details,
},
error.toHumanMessage(),
);
}

throw error;
}
```

<table>
<thead align=left>
<tr>
<th>
name
</th>
<th>
type
</th>
<th width=100%>
description
</th>
</tr>
</thead>
<tbody align=left valign=top>
<tr>
<th>
<code>name</code>
</th>
<td>
<code>constant</code>
</td>
<td><code>GitHubProjectInvalidValueError</code></td>
</tr>
<tr>
<th>
<code>message</code>
</th>
<td>
<code>constant</code>
</td>
<td>

> User value is incompatible with project field type
</td>
<tr>
<th>
<code>details</code>
</th>
<td>
<code>object</code>
</td>
<td>

Object with error details

</td>
</tr>
<tr>
<th>
<code>details.field</code>
</th>
<td>
<code>object</code>
</td>
<td>

Object with field details

</td>
</tr>
<tr>
<th>
<code>details.field.id</code>
</th>
<td>
<code>string</code>
</td>
<td>

`details.field.id` is the project field GraphQL node ID

</td>
</tr>
<tr>
<th>
<code>details.field.name</code>
</th>
<td>
<code>string</code>
</td>
<td>

The field name as shown in the project

</td>
</tr>
<tr>
<th>
<code>details.field.type</code>
</th>
<td>
<code>string</code>
</td>
<td>

Is always either `DATE`, `NUMBER`, or `SINGLE_SELECT`. If it's `SINGLE_SELECT`, then the error is a [`GitHubProjectUnknownFieldOptionError`](#githubprojectunknownfieldoptionerror).

</td>
</tr>
<tr>
<th>
<code>details.userValue</code>
</th>
<td>
<code>string</code>
</td>
<td>

The stringified value set in the API call.

</td>
</tr>
</tbody>
</table>

Example for `error.toHumanMessage()`:

> "unknown" is not compatible with the "My Date" project field
#### `GitHubProjectUnknownFieldOptionError`

Thrown when attempting to set a single select project field to a value that is not included in the field's configured options. Inherits from [`GitHubProjectInvalidValueError`](#githubprojectinvalidvalueerror).

```js
import Project, { GitHubProjectUnknownFieldOptionError } from "github-project";

Expand Down Expand Up @@ -1358,6 +1508,19 @@ Object with field details

The field name as shown in the project

</td>
</tr>
<tr>
<th>
<code>details.field.type</code>
</th>
<td>
<code>constant</code>
</td>
<td>

`SINGLE_SELECT`

</td>
</tr>
<tr>
Expand Down
16 changes: 14 additions & 2 deletions api/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,21 @@ export class GitHubProjectUnknownFieldError extends GitHubProjectError {
}
}

export class GitHubProjectUnknownFieldOptionError extends GitHubProjectError {
export class GitHubProjectInvalidValueError extends GitHubProjectError {
constructor(details) {
super("Project field option cannot be found");
super("User value is incompatible with project field type");
this.details = details;
}

toHumanMessage() {
return `"${this.details.userValue}" is not compatible with the "${this.details.field.name}" project field which expects a value of type "${this.details.field.type}"`;
}
}

export class GitHubProjectUnknownFieldOptionError extends GitHubProjectInvalidValueError {
constructor(details) {
super(details);
this.message = "Project field option cannot be found";
this.details = details;
}

Expand Down
19 changes: 9 additions & 10 deletions api/lib/get-fields-update-query-and-fields.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,16 +197,15 @@ function findFieldOptionIdAndValue(state, field, value) {
return { name, id };
});

throw Object.assign(
new GitHubProjectUnknownFieldOptionError({
field: {
id: field.id,
name: field.name,
options,
},
userValue: value,
})
);
throw new GitHubProjectUnknownFieldOptionError({
field: {
id: field.id,
name: field.name,
type: "SINGLE_SELECT",
options,
},
userValue: value,
});
}

return { id: optionId, value: optionValue };
Expand Down
33 changes: 29 additions & 4 deletions api/lib/update-project-item-fields.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @ts-check

import { GitHubProjectInvalidValueError } from "../../index.js";
import { getFieldsUpdateQueryAndFields } from "./get-fields-update-query-and-fields.js";
import { getStateWithProjectFields } from "./get-state-with-project-fields.js";

Expand Down Expand Up @@ -29,10 +30,34 @@ export async function updateItemFields(project, state, itemNodeId, fields) {

const result = getFieldsUpdateQueryAndFields(stateWithFields, existingFields);

await project.octokit.graphql(result.query, {
projectId: stateWithFields.id,
itemId: itemNodeId,
});
try {
await project.octokit.graphql(result.query, {
projectId: stateWithFields.id,
itemId: itemNodeId,
});
} catch (error) {
const isInvalidValueError =
error?.response?.errors?.[0]?.extensions?.code ===
"argumentLiteralsIncompatible";

/* c8 ignore next */
if (!isInvalidValueError) throw error;

const key = error.response.errors[0].path[1];
const field = stateWithFields.fields[key];

throw new GitHubProjectInvalidValueError({
userValue: fields[key],
field: {
// @ts-expect-error
id: field.id,
// @ts-expect-error
name: field.name,
// @ts-expect-error
type: field.dataType,
},
});
}

return result.fields;
}
18 changes: 18 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,11 +338,29 @@ export declare class GitHubProjectUnknownFieldError<
constructor(details: TDetails);
}

type GitHubProjectInvalidValueErrorDetails = {
userValue: string;
field: {
id: string;
name: string;
type: "NUMBER" | "DATE" | "SINGLE_SELECT";
};
};

export declare class GitHubProjectInvalidValueError<
TDetails extends GitHubProjectInvalidValueErrorDetails,
> extends GitHubProjectError {
name: "GitHubProjectInvalidValueError";
details: TDetails;
constructor(details: TDetails);
}

type GitHubProjectUnknownFieldOptionErrorDetails = {
userValue: string;
field: {
id: string;
name: string;
type: "SINGLE_SELECT";
options: {
id: string;
name: string;
Expand Down
18 changes: 18 additions & 0 deletions index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import GitHubProject, {
GitHubProjectError,
GitHubProjectNotFoundError,
GitHubProjectUnknownFieldError,
GitHubProjectInvalidValueError,
GitHubProjectUnknownFieldOptionError,
GitHubProjectUpdateReadOnlyFieldError,
} from "./index";
Expand Down Expand Up @@ -682,11 +683,28 @@ export function testGitHubProjectUnknownFieldError() {
expectType<string>(error.toHumanMessage());
}

export function testGitHubProjectInvalidValueError() {
const details = {
field: {
id: "field id",
name: "field name",
type: "DATE" as const,
},
userValue: "invalid",
};
const error = new GitHubProjectInvalidValueError(details);

expectType<"GitHubProjectInvalidValueError">(error.name);
expectType<typeof details>(error.details);
expectType<string>(error.toHumanMessage());
}

export function testGitHubProjectUnknownFieldOptionError() {
const details = {
field: {
id: "field id",
name: "field name",
type: "SINGLE_SELECT" as const,
options: [
{
id: "option id",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function test(project) {
},
(error) => ({
error,
// humanMessage: error.toHumanMessage(),
humanMessage: error.toHumanMessage(),
})
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// @ts-check

import { GitHubProjectInvalidValueError } from "../../../index.js";

/**
* @param {import("../../../").default} project
* @param {string} [itemId]
Expand All @@ -12,6 +14,8 @@ export function test(project, itemId = "PVTI_1") {
(error) => ({
error,
humanMessage: error.toHumanMessage(),
isInstanceOfGitHubProjectInvalidValueError:
error instanceof GitHubProjectInvalidValueError,
})
);
}

0 comments on commit c3afa6e

Please sign in to comment.