urbit/app/gh.hoon

327 lines
9.4 KiB
Plaintext
Raw Normal View History

2016-04-13 00:50:41 +03:00
:: This is a connector for the Github API v3.
2016-01-28 00:50:06 +03:00
::
:: You can interact with this in a few different ways:
::
2016-04-13 00:50:41 +03:00
:: - .^({type} %gx /=gh={/endpoint}) to read data or
:: .^(arch %gy /=gh={/endpoint}) to explore the possible
:: endpoints.
2016-01-28 00:50:06 +03:00
::
2016-04-13 00:50:41 +03:00
:: - subscribe to /listen/{owner}/{repo}/{events...} for
:: webhook-powered event notifications. For event list, see
:: https://developer.github.com/webhooks/.
2016-01-28 00:50:06 +03:00
::
2016-04-13 00:50:41 +03:00
:: This is written with the standard structure for api
:: connectors, as described in lib/connector.hoon.
2016-01-29 03:05:36 +03:00
::
2016-02-03 02:41:04 +03:00
/? 314
2016-03-30 00:17:45 +03:00
/- gh, plan-acct
2016-04-08 22:47:11 +03:00
/+ gh-parse, connector
2016-02-03 02:41:04 +03:00
::
!:
2016-01-21 02:06:50 +03:00
=> |%
++ move (pair bone card)
2016-04-13 00:50:41 +03:00
++ card
$% {$diff sub-result}
{$them wire (unit hiss)}
{$hiss wire {$~ $~} $httr {$hiss hiss}}
==
::
:: Types of results we produce to subscribers.
::
2016-01-21 02:06:50 +03:00
++ sub-result
$% {$arch arch}
2016-04-06 03:51:59 +03:00
{$gh-issue issue:gh}
2016-04-06 01:52:48 +03:00
{$gh-list-issues (list issue:gh)}
{$gh-issues issues:gh}
{$gh-issue-comment issue-comment:gh}
{$json json}
{$null $~}
2016-01-21 02:06:50 +03:00
==
2016-04-13 00:50:41 +03:00
::
:: Types of webhooks we expect.
::
2016-01-28 00:50:06 +03:00
++ hook-response
$% {$gh-issues issues:gh}
{$gh-issue-comment issue-comment:gh}
2016-01-28 00:50:06 +03:00
==
2016-01-21 02:06:50 +03:00
--
2016-04-13 00:50:41 +03:00
=+ connector=(connector move sub-result) :: Set up connector library
2016-02-03 02:41:04 +03:00
::
2016-04-13 00:50:41 +03:00
|_ $: hid/bowl
hook/(map @t {id/@t listeners/(set bone)}) :: map events to listeners
==
:: ++ prep _`. :: Clear state when code changes
2016-01-28 00:50:06 +03:00
::
2016-04-06 03:44:04 +03:00
:: List of endpoints
::
++ places
|= wir/wire
2016-04-08 22:47:11 +03:00
^- (list place:connector)
2016-04-13 00:50:41 +03:00
=+ (helpers:connector ost.hid wir "https://api.github.com")
=> |% :: gh-specific helpers
2016-05-04 02:26:52 +03:00
++ read-sentinel
|=(pax/path [ost %diff %arch `0vsen.tinel ~])
::
2016-04-13 00:50:41 +03:00
++ sigh-list-issues-x
|= jon/json
%+ bind ((ar:jo issue:gh-parse) jon)
|= issues/(list issue:gh)
gh-list-issues+issues
::
++ sigh-list-issues-y
|= jon/json
%+ bind ((ar:jo issue:gh-parse) jon)
|= issues/(list issue:gh)
:- `(shax (jam issues))
2016-05-04 02:26:52 +03:00
%- malt ^- (list {@ta $~})
:- [%gh-list-issues ~]
(turn issues |=(issue:gh [(rsh 3 2 (scot %ui number)) ~]))
2016-04-13 00:50:41 +03:00
--
:~ ^- place :: /
:* guard=$~
read-x=read-null
read-y=(read-static %issues ~)
sigh-x=sigh-strange
sigh-y=sigh-strange
==
^- place :: /issues
:* guard={$issues $~}
read-x=read-null
read-y=(read-static %mine %by-repo ~)
sigh-x=sigh-strange
sigh-y=sigh-strange
==
^- place :: /issues/mine
:* guard={$issues $mine $~}
read-x=(read-get /issues)
2016-05-04 02:26:52 +03:00
read-y=(read-static %gh-list-issues ~)
sigh-x=sigh-list-issues-x
sigh-y=sigh-list-issues-y
==
^- place :: /issues/mine/<mark>
:* guard={$issues $mine @t $~}
read-x=read-null
read-y=read-sentinel
2016-04-13 00:50:41 +03:00
sigh-x=sigh-list-issues-x
sigh-y=sigh-list-issues-y
==
^- place :: /issues/by-repo
:* guard={$issues $by-repo $~}
read-x=read-null
^= read-y
|= pax/path
=+ /(scot %p our.hid)/home/(scot %da now.hid)/web/plan
=+ .^({* acc/(map knot plan-acct)} %cx -)
::
((read-static usr:(~(got by acc) %github) ~) pax)
sigh-x=sigh-strange
sigh-y=sigh-strange
==
^- place :: /issues/by-repo/<user>
:* guard={$issues $by-repo @t $~}
read-x=read-null
read-y=|=(pax/path (get /users/[-.+>.pax]/repos))
sigh-x=sigh-strange
^= sigh-y
|= jon/json
%+ bind ((ar:jo repository:gh-parse) jon)
|= repos/(list repository:gh)
[~ (malt (turn repos |=(repository:gh [name ~])))]
==
^- place :: /issues/by-repo/<user>/<repo>
:* guard={$issues $by-repo @t @t $~}
read-x=|=(pax/path (get /repos/[-.+>.pax]/[-.+>+.pax]/issues))
read-y=|=(pax/path (get /repos/[-.+>.pax]/[-.+>+.pax]/issues))
sigh-x=sigh-list-issues-x
sigh-y=sigh-list-issues-y
==
2016-05-04 02:26:52 +03:00
^- place :: /issues/by-repo/<user>/<repo>/<number>
2016-04-13 00:50:41 +03:00
:* guard={$issues $by-repo @t @t @t $~}
^= read-x
|=(pax/path (get /repos/[-.+>.pax]/[-.+>+.pax]/issues/[-.+>+>.pax]))
::
2016-05-04 02:26:52 +03:00
^= read-y
|= pax/path
%. pax
?: ((sane %tas) -.+>+>.pax)
read-sentinel
(read-static %gh-issue ~)
::
2016-04-13 00:50:41 +03:00
^= sigh-x
|= jon/json
%+ bind (issue:gh-parse jon)
|= issue/issue:gh
gh-issue+issue
::
sigh-y=sigh-strange
==
2016-05-04 02:26:52 +03:00
^- place :: /issues/by-repo/<u>/<r>/<n>/<mark>
:* guard={$issues $by-repo @t @t @t @t $~}
read-x=read-null
read-y=read-sentinel
sigh-x=sigh-strange
sigh-y=sigh-strange
==
2016-04-13 00:50:41 +03:00
==
::
:: When a peek on a path blocks, ford turns it into a peer on
:: /scry/{care}/{path}. You can also just peer to this
:: directly.
::
:: We hand control to ++scry.
::
++ peer-scry
|= pax/path
^- {(list move) _+>.$}
?> ?=({care *} pax)
:_ +>.$ :_ ~
(read:connector ost.hid (places %read pax) i.pax t.pax)
::
:: HTTP response. We make sure the response is good, then
:: produce the result (as JSON) to whoever sent the request.
::
++ sigh-httr
|= {way/wire res/httr}
^- {(list move) _+>.$}
?. ?=({$read care @ *} way)
~& res=res
[~ +>.$]
=* style i.way
=* ren i.t.way
=* pax t.t.way
:_ +>.$ :_ ~
:+ ost.hid %diff
(sigh:connector (places ren style pax) ren pax res)
::
:: HTTP error. We just print it out, though maybe we should
:: also produce a result so that the request doesn't hang?
::
++ sigh-tang
|= {way/wire tan/tang}
^- {(list move) _+>.$}
2016-04-20 19:47:30 +03:00
%- (slog >%gh-sigh-tang< tan)
[[ost.hid %diff null+~]~ +>.$]
2016-04-13 00:50:41 +03:00
::
:: We can't actually give the response to pretty much anything
:: without blocking, so we just block unconditionally.
::
++ peek
|= {ren/@tas tyl/path}
^- (unit (unit (pair mark *)))
~ ::``noun/[ren tyl]
::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Webhook-powered event streams (/listen) ::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: To listen to a webhook-powered stream of events, subscribe
:: to /listen/<user>/<repo>/<events...>
::
:: We hand control to ++listen.
::
++ peer-listen
|= pax/path
^- {(list move) _+>.$}
?. ?=({@ @ *} pax)
~& [%bad-listen-path pax]
[~ +>.$]
(listen pax)
2016-04-06 03:44:04 +03:00
::
2016-04-07 03:44:32 +03:00
:: This core handles event subscription requests by starting or
:: updating the webhook flow for each event.
::
++ listen
|= pax/path
=| mow/(list move)
=< abet:listen
|%
++ abet :: Resolve core.
^- {(list move) _+>.$}
[(flop mow) +>.$]
::
++ send-hiss :: Send a hiss
|= hiz/hiss
^+ +>
=+ wir=`wire`[%x %listen pax]
+>.$(mow [[ost.hid %hiss wir `~ %httr [%hiss hiz]] mow])
2016-01-28 00:50:06 +03:00
::
:: Create or update a webhook to listen for a set of events.
2016-01-29 03:05:36 +03:00
::
2016-01-21 02:06:50 +03:00
++ listen
2016-01-26 04:13:08 +03:00
^+ .
2016-04-07 03:44:32 +03:00
=+ pax=pax :: TMI-proofing
?> ?=({@ @ *} pax)
=+ events=t.t.pax
|- ^+ +>+.$
2016-01-28 00:50:06 +03:00
?~ events
+>+.$
2016-01-28 00:50:06 +03:00
?: (~(has by hook) i.events)
2016-04-13 00:50:41 +03:00
$(+>+ (update-hook i.events), events t.events)
$(+>+ (create-hook i.events), events t.events)
2016-01-28 00:50:06 +03:00
::
:: Set up a webhook.
2016-01-29 03:05:36 +03:00
::
2016-01-21 02:06:50 +03:00
++ create-hook
|= event/@t
2016-01-26 04:13:08 +03:00
^+ +>
2016-04-07 03:44:32 +03:00
?> ?=({@ @ *} pax)
=+ clean-event=`tape`(turn (trip event) |=(a/@tD ?:(=('_' a) '-' a)))
2016-01-28 00:50:06 +03:00
=. hook
%+ ~(put by hook) (crip clean-event)
=+ %+ fall
(~(get by hook) (crip clean-event))
*{id/@t listeners/(set bone)}
2016-01-28 00:50:06 +03:00
[id (~(put in listeners) ost.hid)]
2016-01-26 04:13:08 +03:00
%- send-hiss
2016-01-29 03:05:36 +03:00
:* %+ scan
2016-04-07 03:44:32 +03:00
=+ [(trip i.pax) (trip i.t.pax)]
2016-01-21 02:06:50 +03:00
"https://api.github.com/repos/{-<}/{->}/hooks"
auri:epur
2016-01-26 22:03:31 +03:00
%post ~ ~
2016-01-21 02:06:50 +03:00
%- taco %- crip %- pojo %- jobe :~
name+s+%web
active+b+&
events+a+~[s+event] ::(turn `(list ,@t)`t.t.pax |=(a=@t s/a))
2016-01-21 02:06:50 +03:00
:- %config
%- jobe :~
2016-01-28 00:50:06 +03:00
=+ =+ clean-event
"http://107.170.195.5:8443/~/to/gh/gh-{-}.json?anon&wire=/"
[%url s+(crip -)]
[%'content_type' s+%json]
2016-01-21 02:06:50 +03:00
==
==
==
2016-01-28 00:50:06 +03:00
::
:: Add current bone to the list of subscribers for this event.
2016-01-29 03:05:36 +03:00
::
2016-01-21 02:06:50 +03:00
++ update-hook
|= event/@t
2016-01-26 04:13:08 +03:00
^+ +>
=+ hok=(~(got by hook) event)
%_ +>.$
hook
%+ ~(put by hook) event
hok(listeners (~(put in listeners.hok) ost.hid))
2016-01-21 02:06:50 +03:00
==
--
::
2016-02-03 02:41:04 +03:00
:: Pokes that aren't caught in more specific arms are handled
:: here. These should be only from webhooks firing, so if we
:: get any mark that we shouldn't get from a webhook, we reject
:: it. Otherwise, we spam out the event to everyone who's
:: listening for that event.
2016-01-29 03:05:36 +03:00
::
2016-01-28 00:50:06 +03:00
++ poke
|= response/hook-response
^- {(list move) _+>.$}
2016-01-28 00:50:06 +03:00
=+ hook-data=(~(get by hook) (rsh 3 3 -.response))
?~ hook-data
~& [%strange-hook hook response]
2016-02-03 02:41:04 +03:00
[~ +>.$]
:: ~& response=response
2016-01-28 00:50:06 +03:00
:_ +>.$
%+ turn (~(tap in listeners.u.hook-data))
|= ost/bone
2016-01-28 00:50:06 +03:00
[ost %diff response]
2016-01-21 02:06:50 +03:00
--