1
0
Fork 0

feat(config): validate using json schema, compare issue labels for update

This commit is contained in:
ssube 2020-08-15 18:50:39 -05:00
parent 8aaa3976d8
commit 272a566acb
Signed by: ssube
GPG Key ID: 3EED7B957D362AF1
16 changed files with 206 additions and 48 deletions

View File

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [cautious-journey](./cautious-journey.md) &gt; [ChangeSet](./cautious-journey.changeset.md) &gt; [adds](./cautious-journey.changeset.adds.md)
## ChangeSet.adds property
<b>Signature:</b>
```typescript
adds: Array<LabelRef>;
```

22
docs/api/cautious-journey.changeset.md generated Normal file
View File

@ -0,0 +1,22 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [cautious-journey](./cautious-journey.md) &gt; [ChangeSet](./cautious-journey.changeset.md)
## ChangeSet interface
A set of labels to add and/or remove.
<b>Signature:</b>
```typescript
export interface ChangeSet
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [adds](./cautious-journey.changeset.adds.md) | Array&lt;LabelRef&gt; | |
| [removes](./cautious-journey.changeset.removes.md) | Array&lt;LabelRef&gt; | |
| [requires](./cautious-journey.changeset.requires.md) | Array&lt;LabelRef&gt; | Required labels for this state change to occur. |

View File

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [cautious-journey](./cautious-journey.md) &gt; [ChangeSet](./cautious-journey.changeset.md) &gt; [removes](./cautious-journey.changeset.removes.md)
## ChangeSet.removes property
<b>Signature:</b>
```typescript
removes: Array<LabelRef>;
```

View File

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [cautious-journey](./cautious-journey.md) &gt; [StateChange](./cautious-journey.statechange.md) &gt; [matches](./cautious-journey.statechange.matches.md)
[Home](./index.md) &gt; [cautious-journey](./cautious-journey.md) &gt; [ChangeSet](./cautious-journey.changeset.md) &gt; [requires](./cautious-journey.changeset.requires.md)
## StateChange.matches property
## ChangeSet.requires property
Required labels for this state change to occur.
<b>Signature:</b>
```typescript
matches: Array<LabelRef>;
requires: Array<LabelRef>;
```

View File

@ -23,11 +23,11 @@
| Interface | Description |
| --- | --- |
| [ChangeSet](./cautious-journey.changeset.md) | A set of labels to add and/or remove. |
| [Remote](./cautious-journey.remote.md) | Basic functions which every remote API must provide. |
| [RemoteOptions](./cautious-journey.remoteoptions.md) | |
| [ResolveInput](./cautious-journey.resolveinput.md) | Collected inputs for a resolver run. |
| [ResolveResult](./cautious-journey.resolveresult.md) | Collected results from a resolver run. |
| [StateChange](./cautious-journey.statechange.md) | The transition between two state values. |
| [StateLabel](./cautious-journey.statelabel.md) | Grouped labels: the equivalent of a radio group. |
| [StateValue](./cautious-journey.statevalue.md) | One of many values for a particular state. |
| [SyncOptions](./cautious-journey.syncoptions.md) | |

View File

@ -1,21 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [cautious-journey](./cautious-journey.md) &gt; [StateChange](./cautious-journey.statechange.md)
## StateChange interface
The transition between two state values.
<b>Signature:</b>
```typescript
export interface StateChange extends ChangeSet
```
<b>Extends:</b> ChangeSet
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [matches](./cautious-journey.statechange.matches.md) | Array&lt;LabelRef&gt; | Required labels for this state change to occur. |

View File

@ -9,5 +9,5 @@ State changes that could occur to this value.
<b>Signature:</b>
```typescript
becomes: Array<StateChange>;
becomes: Array<ChangeSet>;
```

View File

@ -11,11 +11,11 @@ One of many values for a particular state.
```typescript
export interface StateValue extends BaseLabel, ChangeSet
```
<b>Extends:</b> BaseLabel, ChangeSet
<b>Extends:</b> BaseLabel, [ChangeSet](./cautious-journey.changeset.md)
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [becomes](./cautious-journey.statevalue.becomes.md) | Array&lt;[StateChange](./cautious-journey.statechange.md)<!-- -->&gt; | State changes that could occur to this value. |
| [becomes](./cautious-journey.statevalue.becomes.md) | Array&lt;[ChangeSet](./cautious-journey.changeset.md)<!-- -->&gt; | State changes that could occur to this value. |

View File

@ -1,7 +1,9 @@
import Ajv from 'ajv';
import { LogLevel, NullLogger } from 'noicejs';
import { FlagLabel, StateLabel } from '../labels';
import { RemoteOptions } from '../remote';
import * as SCHEMA_DATA from './schema.yml';
/**
* Config data for the app, loaded from CLI or DOM.
@ -64,3 +66,21 @@ export function initConfig(): ConfigData {
}],
};
}
export const SCHEMA_OPTIONS: Ajv.Options = {
allErrors: true,
coerceTypes: 'array',
missingRefs: 'fail',
removeAdditional: 'failing',
schemaId: 'auto',
useDefaults: true,
verbose: true,
};
export function validateConfig(it: unknown): it is ConfigData {
const ajv = new Ajv(SCHEMA_OPTIONS);
ajv.addSchema(SCHEMA_DATA, 'cautious-journey');
return ajv.validate('cautious-journey#/definitions/config', it) === true;
}

102
src/config/schema.yml Normal file
View File

@ -0,0 +1,102 @@
$schema: "http://json-schema.org/schema#"
$id: cautious-journey
definitions:
label-ref:
type: object
properties:
name:
type: string
change-set:
type: object
properties:
adds:
type: array
items:
$ref: "#/definitions/label-ref"
default: []
removes:
type: array
items:
$ref: "#/definitions/label-ref"
default: []
base-label:
type: object
properties:
color:
type: string
desc:
type: string
default: ''
name:
type: string
requires:
type: array
items:
$ref: "#/definitions/label-ref"
default: []
flag-label:
allOf:
- $ref: "#/definitions/change-set"
- $ref: "#/definitions/base-label"
state-label:
allOf:
- $ref: "#/definitions/change-set"
- $ref: "#/definitions/base-label"
- type: object
properties:
values:
type: array
items:
$ref: "#/definitions/state-value"
default: []
state-value:
allOf:
- $ref: "#/definitions/change-set"
- $ref: "#/definitions/base-label"
- type: object
properties:
becomes:
type: array
default: []
config:
type: object
properties:
logger:
type: object
properties:
level:
type: string
name:
type: string
projects:
type: array
items:
type: object
properties:
colors:
type: array
items:
type: string
flags:
type: array
items:
$ref: "#/definitions/flag-label"
default: []
name:
type: string
remote:
type: object
states:
type: array
items:
$ref: "#/definitions/state-label"
default: []
type: object

View File

@ -1,6 +1,6 @@
import { main } from './main';
export { FlagLabel, StateChange, StateLabel, StateValue } from './labels';
export { ChangeSet, FlagLabel, StateLabel, StateValue } from './labels';
export { Remote, RemoteOptions } from './remote';
export { GithubRemote } from './remote/github';
export { GitlabRemote } from './remote/gitlab';

View File

@ -16,6 +16,11 @@ export interface LabelRef {
export interface ChangeSet {
adds: Array<LabelRef>;
removes: Array<LabelRef>;
/**
* Required labels for this state change to occur.
*/
requires: Array<LabelRef>;
}
/**
@ -41,8 +46,6 @@ export interface BaseLabel extends ChangeSet {
* Label priority.
*/
priority: number;
requires: Array<LabelRef>;
}
/**
@ -50,16 +53,6 @@ export interface BaseLabel extends ChangeSet {
*/
export type FlagLabel = BaseLabel;
/**
* The transition between two state values.
*/
export interface StateChange extends ChangeSet {
/**
* Required labels for this state change to occur.
*/
matches: Array<LabelRef>;
}
/**
* One of many values for a particular state.
*/
@ -67,7 +60,7 @@ export interface StateValue extends BaseLabel, ChangeSet {
/**
* State changes that could occur to this value.
*/
becomes: Array<StateChange>;
becomes: Array<ChangeSet>;
}
/**

View File

@ -5,7 +5,7 @@ import { DEFAULT_SAFE_SCHEMA, safeLoad } from 'js-yaml';
import { join } from 'path';
import { alea } from 'seedrandom';
import { ConfigData } from './config';
import { ConfigData, validateConfig } from './config';
import { Commands, createParser } from './config/args';
import { BunyanLogger } from './logger/bunyan';
import { GithubRemote } from './remote/github';
@ -36,7 +36,7 @@ async function loadConfig(path: string): Promise<ConfigData> {
});
const config = safeLoad(rawConfig, { schema });
if (isNil(config) || typeof config === 'string') {
if (!validateConfig(config)) {
throw new Error();
}

View File

@ -112,7 +112,7 @@ export class GithubRemote implements Remote {
const repo = await mustExist(this.request).issues.listForRepo(path);
for (const issue of repo.data) {
issues.push({
issue: issue.id.toString(10),
issue: issue.number.toString(),
labels: issue.labels.map((l) => l.name),
name: issue.title,
project: options.project,

View File

@ -5,7 +5,7 @@ import { prng } from 'seedrandom';
import { FlagLabel, getLabelColor, getLabelNames, getValueName, StateLabel } from './labels';
import { LabelUpdate, Remote } from './remote';
import { resolveLabels } from './resolve';
import { defaultTo, defaultUntil } from './utils';
import { defaultTo, defaultUntil, compareItems } from './utils';
export interface SyncOptions {
/**
@ -45,8 +45,11 @@ export async function syncIssueLabels(options: SyncOptions): Promise<unknown> {
states: options.states,
});
options.logger.debug({ changes, errors, issue, labels }, 'resolved labels');
// TODO: prompt user to update this particular issue
if (changes.length > 0 && errors.length === 0) {
const sameLabels = !compareItems(issue.labels, labels) || changes.length > 0;
if (sameLabels && errors.length === 0) {
options.logger.info({ issue, labels }, 'updating issue');
await options.remote.updateIssue({
...issue,
@ -143,7 +146,7 @@ export async function syncLabelDiff(options: SyncOptions, oldLabel: LabelUpdate,
project: options.project,
};
options.logger.debug({ body, newLabel, oldLabel, options }, 'update label');
options.logger.debug({ body, newLabel, oldLabel }, 'update label');
const resp = await options.remote.updateLabel(body);

View File

@ -18,3 +18,20 @@ export function randomItem<T>(items: Array<T>, source: prng): T {
const idx = Math.abs(source.int32()) % items.length;
return items[idx];
}
export function compareItems<T>(a: Array<T>, b: Array<T>): boolean {
if (a.length !== b.length) {
return false;
}
const aSorted = a.sort();
const bSorted = b.sort();
for (let i = 0; i < aSorted.length; ++i) {
if (aSorted[i] !== bSorted[i]) {
return false;
}
}
return true;
}