generated from actions/typescript-action
-
Notifications
You must be signed in to change notification settings - Fork 2
/
model.ts
174 lines (153 loc) 路 4.93 KB
/
model.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import { context } from '@actions/github'
import { Schema } from '@hapi/joi'
import { IssuesCreateResponse } from '@octokit/rest'
import { Hook, HookSingular } from 'before-after-hook'
import uuid from 'uuid'
import octokit from './octokit'
import Instance, { IssueRecord } from './instance'
type HookFn = (opts: any) => Promise<void>
interface Hooks {
beforeValidate?: HookFn
afterValidate?: HookFn
beforeCreate?: HookFn
afterCreate?: HookFn
beforeSave?: HookFn
afterSave?: HookFn
}
export interface ModelInput {
name: string
schema: Schema
hooks?: Hooks
}
export default class Model {
readonly name: string
readonly schema: Schema
private hooks: { [key: string]: HookSingular<any, any, any> }
private hookMap: { [key in keyof Hooks]: Function }
constructor (model: ModelInput) {
this.name = model.name
this.schema = model.schema
// An object of Hook instances
this.hooks = {
create: new Hook.Singular(),
validate: new Hook.Singular(),
save: new Hook.Singular()
}
// A map of hook names to actual setters
this.hookMap = {
beforeCreate: this.hooks.create.before,
afterCreate: this.hooks.create.after,
beforeValidate: this.hooks.validate.after,
afterValidate: this.hooks.validate.after,
beforeSave: this.hooks.save.after,
afterSave: this.hooks.save.after,
}
// Actually register the hooks
if (model.hooks) {
for (const key in model.hooks) {
const hookFn = model.hooks[key as keyof Hooks]
if (!hookFn) throw new Error(`No hook function for ${key} was provided`)
this.registerHook(key as keyof Hooks, hookFn)
}
}
}
/**
* Set a before or after hook for an operation.
*/
public registerHook (hookName: keyof Hooks, hookFn: HookFn) {
const hookRegisterer = this.hookMap[hookName]
if (!hookRegisterer) throw new Error(`${hookName} is not a valid hook.`)
return hookRegisterer(hookFn.bind(this))
}
/**
* Convert a `where` object to a string for proper searching.
*/
private whereToStr (where: any) {
const jsonStr = JSON.stringify(where, null, 2)
return jsonStr.slice(1, jsonStr.length - 2)
}
/**
* Call the search API to return all issues with this model's label.
*/
private async searchForIssues (): Promise<IssuesCreateResponse[]> {
// Search for issues by this label
const issues = await octokit.search.issuesAndPullRequests({
q: `is:issue is:open label:${this.name}`
})
return issues.data.items
}
private parseDataFromIssueBody (body: string): any {
const reg = /^`{3}\n([\s\S]+)\n`{3}/
const match = body.match(reg)
if (match && match[1]) return JSON.parse(match[1])
return {}
}
private convertIssueToJson (issue: IssuesCreateResponse): IssueRecord {
const json = this.parseDataFromIssueBody(issue.body)
return {
...json,
created_at: issue.created_at,
issue_number: issue.number
}
}
/**
* Find one record that matches the provided filter object.
*/
async findOne (where: any) {
const issues = await this.searchForIssues()
const whereStr = this.whereToStr(where)
const found = issues.find(issue => (issue.body as string).includes(whereStr))
if (found) return new Instance(this, this.convertIssueToJson(found))
return null
}
/**
* Find all records that match the provided filter object.
*/
async findAll (where?: any): Promise<Instance[]> {
const issues = await this.searchForIssues()
if (where) {
const whereStr = this.whereToStr(where)
const found = issues.filter(issue => (issue.body as string).includes(whereStr))
return found.map(item => new Instance(this, this.convertIssueToJson(item)))
} else {
return issues.map(item => new Instance(this, this.convertIssueToJson(item)))
}
}
/**
* Create a new record
*/
async create (opts: any): Promise<Instance> {
return this.hooks.validate(async () => {
// Validate the provided object against the model's schema
await this.schema.validate(opts)
// Actually go to create the record
return this.hooks.create(async () => {
// Generate a UUID
const id = uuid.v4()
const data = {
action_record_id: id,
...opts
}
// Save the new record to GitHub
return this.hooks.save(async () => {
// Create the new issue
const newIssue = await octokit.issues.create({
...context.repo,
title: `[${this.name}]: ${id}`,
body: Model.jsonToBody(data),
labels: [this.name]
})
// Return the new instance
return new Instance(this, {
...data,
created_at: newIssue.data.created_at,
issue_number: newIssue.data.number
})
}, opts)
}, opts)
}, opts)
}
static jsonToBody (data: any) {
return '```\n' + JSON.stringify(data, null, 2) + '\n```'
}
}