edgurgel / tentacat Goto Github PK
View Code? Open in Web Editor NEWSimple Elixir wrapper for the GitHub API
Home Page: https://hexdocs.pm/tentacat
License: MIT License
Simple Elixir wrapper for the GitHub API
Home Page: https://hexdocs.pm/tentacat
License: MIT License
Almost all software needs tests. This could therefore help a lot of people.
I agree with Jose that mocking is not the best solution (http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/) and therefore think a behaviour-based approach could work best. This means that we have one module which we can exchange in the test environment for another behaviour. Behaviours in elixir can be seen like inheritance in other languages.
So in our tests, we would probably have a genserver store the items (in an :ets-table ?) we need and return them if the business logic asks for them. In dev and production, the tentacat wrapper would ask the other behaviour which works as usual.
Another possibility would be to create a test package just like they are doing here: beam-community/stripity-stripe#124
I will try to find a solution within the next days and would love to get some input here. When I'm finished I'll make a PR to update the readme.
First of all, thanks for your interest in GitHub and creating a client library! However, the Octokit name is being used to represent officially supported libraries, so naming this library octokit
could mislead users into thinking that it's a project by GitHub.
Would you mind changing the name to something other than Octokit? Thanks!
It would be really useful to add in the repo deployment API. I might take a stab at implementing it myself if that's cool with you.
Documentation: https://developer.github.com/v3/repos/deployments/
The example code and "more info" sections appeared to be a bit out of date here: https://hexdocs.pm/tentacat/Tentacat.Issues.html
In particular, filter references list and the github api urls referenced the pulls endpoint.
PR coming right up :)
Is there a reason why not jump to httpoison 1.x?
If not, may I crete the pul request?
Looking at GitHub's Deployments api docs, it describes the ability to filter deployments by "sha", "ref", "task" and "environment". There doesn't seem to be the ability to pass in optional query params to the Tentacat.Repositories.Deployments.list call. This will be convenient, for instance, in the case of finding all deployments associated with a PR/sha value.
Hi, I'm going to drop down to HTTPoison most likely for this, one optimization which can be made in any call is to pass the ETag from the previous invocation - if you do so, and nothing has changed, your rate limit remains unchanged. Could it be passed in the existing options block?
"ETag", ""eaa46eb91c77ded86b4261245e49fbd5"
initially reported in #144 (comment)
it looks like some (possibly all) calls to get
return the error tuple ({integer, any, HTTPoison.Response.t()}
) instead of the success tuple ({:ok, :jsx.json_term(), HTTPoison.Response.t()}
) on successful calls.
Hi!
Just wanting to start creating a PR to support searching for users (its something I am working on atm).
Have cloned repo locally, but when I run
MIX_ENV=test mix do deps.get, test
I get the following errors ๐
1) test followers/2 (Tentacat.FollowersTest)
test/followers_test.exs:33
** (CaseClauseError) no case clause matching: {:error, {:undef, [{:cover, :is_compiled, [:hackney], []}, {:meck_proc, :get_cover_state, 1, [file: 'src/meck_proc.erl', line: 395]}, {:meck_proc, :backup_original, 3, [file: 'src/meck_proc.erl', line: 350]}, {:meck_proc, :init, 1, [file: 'src/meck_proc.erl', line: 204]}, {:gen_server, :init_it, 6, [file: 'gen_server.erl', line: 328]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 240]}]}}
stacktrace:
src/meck_proc.erl:112: :meck_proc.set_expect/2
src/meck.erl:234: :meck.expect/3
(elixir) lib/enum.ex:604: Enum."-each/2-lists^foreach/1-0-"/2
(elixir) lib/enum.ex:604: Enum.each/2
test/followers_test.exs:34
2) test following?/3 (Tentacat.FollowersTest)
test/followers_test.exs:45
** (CaseClauseError) no case clause matching: {:error, {:undef, [{:cover, :is_compiled, [:hackney], []}, {:meck_proc, :get_cover_state, 1, [file: 'src/meck_proc.erl', line: 395]}, {:meck_proc, :backup_original, 3, [file: 'src/meck_proc.erl', line: 350]}, {:meck_proc, :init, 1, [file: 'src/meck_proc.erl', line: 204]}, {:gen_server, :init_it, 6, [file: 'gen_server.erl', line: 328]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 240]}]}}
stacktrace:
src/meck_proc.erl:112: :meck_proc.set_expect/2
src/meck.erl:234: :meck.expect/3
(elixir) lib/enum.ex:604: Enum."-each/2-lists^foreach/1-0-"/2
(elixir) lib/enum.ex:604: Enum.each/2
test/followers_test.exs:46
3) test unfollow/2 (Tentacat.FollowersTest)
test/followers_test.exs:57
** (CaseClauseError) no case clause matching: {:error, {:undef, [{:cover, :is_compiled, [:hackney], []}, {:meck_proc, :get_cover_state, 1, [file: 'src/meck_proc.erl', line: 395]}, {:meck_proc, :backup_original, 3, [file: 'src/meck_proc.erl', line: 350]}, {:meck_proc, :init, 1, [file: 'src/meck_proc.erl', line: 204]}, {:gen_server, :init_it, 6, [file: 'gen_server.erl', line: 328]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 240]}]}}
stacktrace:
src/meck_proc.erl:112: :meck_proc.set_expect/2
src/meck.erl:234: :meck.expect/3
(elixir) lib/enum.ex:604: Enum."-each/2-lists^foreach/1-0-"/2
(elixir) lib/enum.ex:604: Enum.each/2
test/followers_test.exs:58
...a bunch more of the same errors go here....
97) test delete/4 (Tentacat.Commits.CommentsTest)
test/commits/comments_test.exs:55
** (CaseClauseError) no case clause matching: {:error, {:undef, [{:cover, :is_compiled, [:hackney], []}, {:meck_proc, :get_cover_state, 1, [file: 'src/meck_proc.erl', line: 395]}, {:meck_proc, :backup_original, 3, [file: 'src/meck_proc.erl', line: 350]}, {:meck_proc, :init, 1, [file: 'src/meck_proc.erl', line: 204]}, {:gen_server, :init_it, 6, [file: 'gen_server.erl', line: 328]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 240]}]}}
stacktrace:
src/meck_proc.erl:112: :meck_proc.set_expect/2
src/meck.erl:234: :meck.expect/3
(elixir) lib/enum.ex:604: Enum."-each/2-lists^foreach/1-0-"/2
(elixir) lib/enum.ex:604: Enum.each/2
test/commits/comments_test.exs:56
..
Finished in 0.7 seconds (0.4s on load, 0.2s on tests)
105 tests, 97 failures, 7 invalid
This is my output from deps.get:
MIX_ENV=test mix do deps.get, test
inch_ex is missing its version requirement, use ">= 0.0.0" if it should match any version
Running dependency resolution
Dependency resolution completed
certifi: 0.3.0
earmark: 0.1.19
ex_doc: 0.8.4
exactor: 2.2.0
excoveralls: 0.4.5
exjsx: 3.2.0
exvcr: 0.7.1
hackney: 1.4.8
httpoison: 0.8.1
idna: 1.0.3
inch_ex: 0.4.0
jsx: 2.6.2
meck: 0.8.4
mimerl: 1.0.2
poison: 1.5.2
ssl_verify_hostname: 1.0.5
New to elixir from ruby, so it might just be my setup, but not sure where to start looking. ๐
It seems that the only methods linked to the labels on issues at the moment are adding and listing labels.
I'm looking to use the remove label api, would this be a good addition to Tentacat?
Based on our Readme, if we want to use decode JSON, we can pass deserialization options following this way:
#To have Atom keys
config :tentacat, :deserialization_options, [labels: :atoms]
But seems like this one is outdated or just a typo with the current version of exjsx
(4.0.0)
https://github.com/talentdeficit/exjsx#decodejson-opts
We have to pass a tuple, not a keyword list. cc @edgurgel please take a look at it if you have time, I will create a PR to fix this one.
Could be just basic but it'd be nice to be able to easily specify the query and the parameters w/ the client and have it run it for you. Something like:
Tentacat.GraphQL.query(@query, %{params: :go_here}, client)
hi again @edgurgel
I saw that you release 0.3.0 and since #48 got merged I just tried to update my project to use tentacat from hex.
Weirdly, it looks like the 0.3.0 tag (and I presume also the pushed release) doesn't have all the commits and misses the merge of #48
https://github.com/edgurgel/tentacat/commits/v0.3.0
let me know if I can do anything else,
thanks!
Using Tentacat for a project and so far its worked wonderfully for almost everything. I'm finally running into an issue creating a new webhook for an organization.
client = Tentacat.Client.new(%{access_token: get_session(conn, :access_token)})
params = %{
name: "web",
active: true,
config: %{
url: Application.get_env(:my_app, GithubHooks)[:webhook_payload_uri],
content_type: "json"
},
events: [
"commit_comment",
"create",
"delete",
"gollum",
"issue_comment",
"issues",
"label",
"milestone",
"project_card",
"project_column",
"project",
"public",
"pull_request",
"pull_request_review",
"pull_request_review_comment",
"release"
]
}
Tentacat.Organizations.Hooks.create("my_org", params, client)
Reponse I get back is:
{404,
%{"documentation_url" => "https://developer.github.com/v3",
"message" => "Not Found"}}
See anything obvious that I'm doing wrong?
Trying to use Tentacat.Issues.list_all
passing a since
parameter like the docs allow but it seems to be impossible... Several other functions seem to not allow this capability.
I've just implemented this in the python github client - this issue serves as a reminder to me, or anyone else who fancies a crack at it - to add this functionality to tentacat.
Link to PyGithub/PyGithub project page
Commit which added and tested this functionality.
Install the feature version :
pip install git+git://github.com/bryanhuntesl/PyGithub.git@esl-local-fix-watches
Grab a github token, set it as an environmental variable and run this snippet:
from github import *
import os
g = Github(login_or_token=os.getenv("GITHUB_TOKEN"))
u=g.get_user()
org=g.get_organization("basho")
for r in org.get_repos():
u.add_to_watched(r)
Now you're subscribed to every currently public basho repository on github
Perform this same operation using tentacat.
GitHub API allows to set Accept header to application/vnd.github.v3.diff and application/vnd.github.v3.patch to return raw diffs and patches. This raises error in Tentacat because response body is always processed by JSX.decode!. Even if it is not json
Currently theres no handling of the rate limit in this project. I regularly hit the rate limit (usually at the end of the hour) and would like to hear your thoughts on how to handle this properly.
Heres the exception I get when that happens:
[error] Task #PID<0.516.0> started from #PID<0.515.0> terminating
** (FunctionClauseError) no function clause matching in Tentacat.process_stream/1
lib/tentacat.ex:102: Tentacat.process_stream({403, %{"documentation_url" => "https://developer.github.com/v3/#rate-limiting", "message" => "API rate limit exceeded for mgwidmann."}})
(elixir) lib/stream.ex:1099: Stream.do_resource/5
(elixir) lib/enum.ex:1486: Enum.reduce/3
(elixir) lib/enum.ex:2248: Enum.to_list/1
I'm trying to use Github's metadata matching to return code snippets/highlights in the response, as documented in https://developer.github.com/v3/search/#text-match-metadata. I know that I can add my own headers in HTTPoison, ie
header_list = Enum.to_list(headers)
[{"Accept", "application/vnd.github.v3.text-match+json"} | header_list]
So I can get back something like this:
...
"object_type": "FileContent",
"property": "content",
"fragment": "\",\n \"key_limit\": 2,\n \"owner\": {\n \"email",
"matches": [
{
"text": "key_limit",
"indices": [
16,
25
]
}
...
Is there a way to pass the header info during the client creation? I tried something like the following, but no luck:
Tentacat.Client.new(%{access_token: "#{token}", accept: "application/vnd.github.v3.text-match+json"}, "#{@uri}")
I also tried wrapping HTTPoison.base to pass the accept header, but I'm assuming that'll only work if I'm using all the HTTPoison base methods directly. Attempts to add text match as a header return results as a regular search api call. Any suggestions?
I'd love to have access to the events within each issue.
Any reason not to add this as a feature?
As you can see here the documentation doesn't mention you can pass in parameters like [sort: "pushed"]
to the function even though that feature has already been implemented.
Due to one of the VCR files having a question mark in the filename:
Feature to add a repo/repos to a team. More info, please see below URL
https://developer.github.com/v3/teams/#add-or-update-team-repository
Sorry, Elixir newbie so I am unable to contribute right now.
For API requests that have a large number of results, we're only returning the first page. Additionally because the headers are thrown away in the process_response
call, we have no way to tell the user, "Hey, there are more pages to what you asked for."
Here's a link
header from the call to Tentacat.Repositories.list_users("jeffweiss")
{"X-GitHub-Media-Type", "github.v3; format=json"},
{"Link",
"<https://api.github.com/user/818053/repos?page=2>; rel=\"next\", <https://api.github.com/user/818053/repos?page=5>; rel=\"last\""},
Hi, I was searching for how to use the endpoint described here in Tentacat, but I wasn't able to find anything. Is is implemented yet?
(not related, the documentation can't be read online, it's dead link)
One surprising thing about using tentacat is that the client argument is always the last item in function argument lists.
From the README, it would be better if we could do:
iex> Tentacat.Client.new |> Tentacat.Users.find "edgurgel", client
Instead of:
iex> client = Tentacat.Client.new
iex> Tentacat.Users.find "edgurgel", client
client = Tentacat.Client.new((%{access_token: "xxx"}))
Tentacat.Repositories.repo_get("elixir-conspiracy", "pacman", client)
** (Protocol.UndefinedError) protocol String.Chars not implemented for %Tentacat.Client...
When there is a 301 response from Github V3 Api an error is raised, in my understanding the redirect would have to be performed transparently.
The error I'm getting:
** (FunctionClauseError) no function clause matching in :lists.flatten/1
2017-09-06T15:20:05.510733+00:00 app[worker.1]: (stdlib) lists.erl:616: :lists.flatten({301, %{"documentation_url" => "https://developer.github.com/v3/#http-redirects", "message" => "Moved Permanently", "url" => "https://api.github.com/repositories/28398133/pulls?state=open"}})
As you can see the "repos" was replaced by "repositories".
I'm running this code:
Tentacat.Pulls.filter(org, repo, %{state: "open"}, client)
See more:
Following on from #57, I'm still having no luck trying to run tests ๐.... I have 15 failures, all along the same line. Heres one:
15) test remove/4 (Tentacat.Pulls.CommentsTest)
test/pulls/comments_test.exs:56
** (ArgumentError) argument error
stacktrace:
(jsx) src/jsx_decoder.erl:234: :jsx_decoder.value/4
(exjsx) lib/jsx.ex:15: JSX.decode!/2
(tentacat) lib/tentacat.ex:13: Tentacat.process_response/1
test/pulls/comments_test.exs:58
Finished in 7.5 seconds (0.5s on load, 6.9s on tests)
105 tests, 15 failures
While trying to solve this I have removed and reinstalled erlang via apt-get according to the Ubuntu instructions here: http://elixir-lang.org/install.html#unix-and-unix-like
justin@linisox:~/projects/tentacat$ elixir -v
Erlang/OTP 18 [erts-7.2] [source-e6dd627] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Elixir 1.2.0
The good news is that I have prototyped the search addition, it seems to work, so will write some tests and post the PR once this is resolved. ๐
It will be really nice to have one library that helps to automate Github Projects workflow (eg list labels and set correct workflow status in GitHub Projects).
Subj.
Reference https://developer.github.com/v3/gists/
it looks like the release has been incremented in the repo (in light of the API change from #132) but it has not been tagged or released. can that be published to hex so we can use it? The standardized return type would be very helpful! thank you!
Brand new Elixir project, with just Tentacat as a dependency:
% iex -S mix
Erlang/OTP 17 [erts-6.4] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.0.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Tentacat.start
{:ok, [:idna, :hackney, :httpoison]}
iex(2)> client = Tentacat.Client.new
** (UndefinedFunctionError) undefined function: Tentacat.Client.new/0
Tentacat.Client.new()
iex(2)>
Thoughts?
Is this the correct library to listen for webhook responses?
Hi @edgurgel,
I am trying to write a test, which call to Tentacat.Repositories.list_mine(client)
using exvcr, but I am getting an error:
stacktrace:
(stdlib) gen.erl:168: :gen.do_call/4
(elixir) lib/gen_server.ex:561: GenServer.call/3
lib/exvcr/handler.ex:24: ExVCR.Handler.get_response_from_cache/2
lib/exvcr/handler.ex:14: ExVCR.Handler.get_response/2
src/meck_code_gen.erl:154: :meck_code_gen.exec/4
(httpoison) lib/httpoison/base.ex:396: HTTPoison.Base.request/9
(tentacat) lib/tentacat.ex:2: Tentacat.request!/5
(tentacat) lib/tentacat.ex:110: Tentacat.request_with_pagination/4
As you use the same lib to record http responses, perhaps you know what's going on here.
Thanks!
I'm porting a script that I wrote with octokit.rb to tentacat, and found an important part (for me) of the gh api missing, specifically, commit comments. I can't find it documented in the github api documentation, but this method is available here:
https://github.com/octokit/octokit.rb/blob/master/lib/octokit/client/commit_comments.rb#L24
Is this something that tentacat would want to support?
Hi!
I'm trying to add the per_page
param to the Tentacat.Search.users
request but no options can be passed for that. Is there any other way of doing it?
I have seen this PR #106 which seems to add this, but it hasn't be merged. Are you planning to merge it?
Thanks in advance
It looks like the rest of the Search API is supported but the search/issues
endpoint isn't, it would be great to have that support :)
It would be good if Tentacat.Repositories.list_mine/1
function also received a set of github options. These options would give it flexibility, and help filtering results -- as sometimes it's necessary.
I forked your repo and made the change, take a look at it:
https://github.com/cabol/tentacat/blob/master/lib/tentacat/repositories.ex#L21
I sent you the PR too.
Thanks.
I'm seeing this from Tentacat.Commits.find
with a GHE using a self-signed CA:
19:02:06.119 [error] SSL: :certify: ssl_handshake.erl:1490:Fatal error: unknown ca
** (HTTPoison.Error) {:tls_alert, 'unknown ca'}
(tentacat) lib/tentacat.ex:2: Tentacat.request!/5
(tentacat) lib/tentacat.ex:42: Tentacat.json_request/5
I believe HTTPoison's request!/5
(which is used in Tentacat, e.g., here) takes some kind of list of options where I could specify a "cacertfile" path...
If not for #65 I'd try that, but is there an easier way to just override some HTTPoison default setting and provide a CA cert in my own project's config.exs
?
Hey @edgurgel! Good job with this wrapper for Github, I've been using it and it's really helpful. Is it ok to use this codebase to create other client APIs? What do you deem necessary, besides including your name on LICENSE
? Thanks!
It's possible to use https://github.com/parroty/exvcr to most requests, except the ones that may have confidential data. Maybe take a look on other libraries...
We need a cool logo/mascott for Tentacat! @jugoncalves โ
exjsx supports https://github.com/talentdeficit/exjsx#exports returning maps with atom keys
but as I see in https://github.com/edgurgel/tentacat/blob/master/lib/tentacat.ex#L11
you don't pass any options to JSX.decode!
.
What about having an application environment setting to allow configuring the keys?
I'm willing to submit a PR if you think it makes sense.
In both Elixir as in with Erlang the convention is that the client is the first parameter of a function.
You should consider doing this as I think it might look nicer especially when the client code is close to other client code that follows this standard practice.
First way to do this is to always a client instance for all calls. Even though the client is an empty/anonymous one.
This other was is that you could use the fact that default attributes can just as easily be the first one as the last. Thus changing:
def find(user, client \\ %Client{}) do
get "users/#{user}", client.auth
end
into:
def find(client \\ %Client{}, user) do
get "users/#{user}", client.auth
end
I know this is a pretty big breaking change. But I really think that code could end up looking nicer that way. ie.:
client = %Tentacat.Client{auth: %{user: "user", password: "password"}}
user = client |> Tentacat.Users.me
client |> Tentacat.Organizations.list(user[:id])
Hi ๐
Is this API method supported?
https://developer.github.com/v3/repos/collaborators/#review-a-users-permission-level
If not, I would like to propose the PR.
Thanks!
C:\code\infr>git clone https://github.com/edgurgel/tentacat.git
Cloning into 'tentacat'...
remote: Counting objects: 1430, done.
rRemote: Total 1430 (delta 0), reused 0 (delta 0), pack-reused 1430eceiving objects: 94% (1345/1430), 164.01 KiR
Receiving objects: 100% (1430/1430), 286.35 KiB | 137.00 KiB/s, done.
Resolving deltas: 100% (797/797), done.
Checking connectivity... done.
fatal: cannot create directory at 'test/fixture/vcr_cassettes/followers#following?': Invalid argument
warning: Clone succeeded, but checkout failed.
You can inspect what was checked out with 'git status'
and retry the checkout with 'git checkout -f HEAD'
This seems to be due to illegal characters in paths, particularly ?
.
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
Tentacat has a bunch of those here:
https://github.com/edgurgel/tentacat/tree/master/test/fixture/vcr_cassettes
๐
Tentacat.Organizations.list_mine(client) # returns tuple
Tentacat.Repositories.list_mine(client) # returns list
How is pagination handled by Tentacat ?
I didn't find any mention of it in the documentation. These tests gave me some insight but I would like a more thorough answer :)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.