From 6274bc58dfe5c175de78ca4c975086558ab90665 Mon Sep 17 00:00:00 2001 From: Matt Casper Date: Wed, 11 May 2016 22:18:07 -0700 Subject: [PATCH] Add the basic Teams API As documented [here](https://developer.github.com/v3/orgs/teams/#teams), this adds the basic organization teams CRUD. If this is all good and gets accepted, I'll probably go on to add the rest of the team API, as it's currently relevant to the project I'm working on :) --- README.md | 3 +- lib/tentacat/organizations/teams.ex | 98 +++++++++++++++++++ .../organizations/teams#create.json | 46 +++++++++ .../organizations/teams#delete.json | 41 ++++++++ .../organizations/teams#find.json | 46 +++++++++ .../organizations/teams#list.json | 45 +++++++++ .../organizations/teams#update.json | 45 +++++++++ test/organizations/teams_test.exs | 56 +++++++++++ 8 files changed, 379 insertions(+), 1 deletion(-) create mode 100644 lib/tentacat/organizations/teams.ex create mode 100644 test/fixture/vcr_cassettes/organizations/teams#create.json create mode 100644 test/fixture/vcr_cassettes/organizations/teams#delete.json create mode 100644 test/fixture/vcr_cassettes/organizations/teams#find.json create mode 100644 test/fixture/vcr_cassettes/organizations/teams#list.json create mode 100644 test/fixture/vcr_cassettes/organizations/teams#update.json create mode 100644 test/organizations/teams_test.exs diff --git a/README.md b/README.md index cb8c6b5..016edce 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Tentacool + Cat = Tentacat * Files * Organizations * Members + * Teams * Users * Emails * Keys @@ -36,7 +37,7 @@ First, add Tentacat to your `mix.exs` dependencies: ```elixir def deps do - [{:tentacat, "~> 0.2"}] + [{:tentacat, "~> 0.5"}] end ``` diff --git a/lib/tentacat/organizations/teams.ex b/lib/tentacat/organizations/teams.ex new file mode 100644 index 0000000..25ed033 --- /dev/null +++ b/lib/tentacat/organizations/teams.ex @@ -0,0 +1,98 @@ +defmodule Tentacat.Organizations.Teams do + import Tentacat, [:except, :delete, 2] + alias Tentacat.Client + + @doc """ + List all public teams for an organization + + ## Example + + Tentacat.Organizations.Teams.list "my_org" + Tentacat.Organizations.Teams.list "my_org", client + + More info at: https://developer.github.com/v3/orgs/teams/#list-teams + """ + @spec list(binary, Client.t) :: Tentacat.response + def list(organization, client \\ %Client{}) do + get "orgs/#{organization}/teams", client + end + + @doc """ + Get a team + + ## Example + + Tentacat.Orgnizations.Teams.find "1485060" + Tentacat.Orgnizations.Teams.find "1485060", client + + More info at: https://developer.github.com/v3/orgs/teams/#get-team + """ + @spec find(integer, Client.t) :: Tentacat.response + def find(team_id, client \\ %Client{}) do + get "teams/#{team_id}", client + end + + @doc """ + Create a team + + Create body example: + + %{ + "name" => "my new team", + "description" => "Team for everyone with commit access", + "repo_names" => [ + "my_org/secret_repo", + "my_org/secret_repo2", + ], + "privacy" => "closed", + } + + ## Example + + Tentacat.Organizations.Teams.create "my_org", body + Tentacat.Organizations.Teams.create "my_org", body, client + + More info at: https://developer.github.com/v3/orgs/teams/#create-team + """ + @spec create(binary, map, Client.t) :: Tentacat.response + def create(organization, body, client \\ %Client{}) do + post "orgs/#{organization}/teams", client, body + end + + @doc """ + Update a team + + Update body example: + + %{ + "name": "Super Admins", + "description": "Team for everyone with commit and admin access", + } + + ## Example + + Tentacat.Organizations.Teams.update "1485060", body + Tentacat.Organizations.Teams.update "1485060", body, client + + More info at: https://developer.github.com/v3/orgs/teams/#edit-team + """ + @spec update(integer, map, Client.t) :: Tentacat.response + def update(team_id, options, client \\ %Client{}) do + patch "teams/#{team_id}", client, options + end + + @doc """ + Delete a team + + ## Example + + Tentacat.Organizations.Teams.delete "1485060" + Tentacat.Organizations.Teams.delete "1485060", client + + More info at: https://developer.github.com/v3/orgs/teams/#delete-team + """ + @spec delete(integer, Client.t) :: Tentacat.response + def delete(team_id, client \\ %Client{}) do + Tentacat.delete "teams/#{team_id}", client + end +end diff --git a/test/fixture/vcr_cassettes/organizations/teams#create.json b/test/fixture/vcr_cassettes/organizations/teams#create.json new file mode 100644 index 0000000..d9536f4 --- /dev/null +++ b/test/fixture/vcr_cassettes/organizations/teams#create.json @@ -0,0 +1,46 @@ +[ + { + "request": { + "body": "{\"description\":\"Team for everyone with commit access\",\"name\":\"my new team\",\"privacy\":\"closed\",\"repo_names\":[\"my_org/secret_repo\",\"my_org/secret_repo2\"]}", + "headers": { + "User-agent": "tentacat", + "Authorization": "token yourtokengoeshere" + }, + "method": "post", + "options": [], + "request_body": "", + "url": "https://api.github.com/orgs/my_org/teams" + }, + "response": { + "body": "{\"name\":\"my new team\",\"id\":2019723,\"slug\":\"my-new-team\",\"description\":\"Team for everyone with commit access\",\"permission\":\"pull\",\"privacy\":\"closed\",\"url\":\"https://api.github.com/teams/2019723\",\"members_url\":\"https://api.github.com/teams/2019723/members{/member}\",\"repositories_url\":\"https://api.github.com/teams/2019723/repos\",\"members_count\":0,\"repos_count\":0,\"organization\":{\"login\":\"my_org\",\"id\":9755201,\"url\":\"https://api.github.com/orgs/my_org\",\"repos_url\":\"https://api.github.com/orgs/my_org/repos\",\"events_url\":\"https://api.github.com/orgs/my_org/events\",\"hooks_url\":\"https://api.github.com/orgs/my_org/hooks\",\"issues_url\":\"https://api.github.com/orgs/my_org/issues\",\"members_url\":\"https://api.github.com/orgs/my_org/members{/member}\",\"public_members_url\":\"https://api.github.com/orgs/my_org/public_members{/member}\",\"avatar_url\":\"https://avatars.githubusercontent.com/u/9755201?v=3\",\"description\":\"\",\"name\":\"\",\"company\":null,\"blog\":\"\",\"location\":\"\",\"email\":\"\",\"public_repos\":2,\"public_gists\":0,\"followers\":0,\"following\":0,\"html_url\":\"https://github.com/my_org\",\"created_at\":\"2014-11-14T22:33:30Z\",\"updated_at\":\"2016-04-30T19:56:09Z\",\"type\":\"Organization\"}}", + "headers": { + "Server": "GitHub.com", + "Date": "Thu, 12 May 2016 05:09:16 GMT", + "Content-Type": "application/json; charset=utf-8", + "Content-Length": "1191", + "Status": "201 Created", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4973", + "X-RateLimit-Reset": "1463030385", + "Cache-Control": "private, max-age=60, s-maxage=60", + "Vary": "Accept, Authorization, Cookie, X-GitHub-OTP", + "ETag": "\"87a56fa1b3cb54b13535b7dc950490e5\"", + "X-OAuth-Scopes": "admin:org", + "X-Accepted-OAuth-Scopes": "admin:org, repo", + "Location": "https://api.github.com/teams/2019723", + "X-GitHub-Media-Type": "github.v3; format=json", + "Access-Control-Expose-Headers": "ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval", + "Access-Control-Allow-Origin": "*", + "Content-Security-Policy": "default-src 'none'", + "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload", + "X-Content-Type-Options": "nosniff", + "X-Frame-Options": "deny", + "X-XSS-Protection": "1; mode=block", + "X-Served-By": "a241e1a8264a6ace03db946c85b92db3", + "X-GitHub-Request-Id": "44073D0C:2CBD:B9E02B9:57340FFC" + }, + "status_code": 201, + "type": "ok" + } + } +] diff --git a/test/fixture/vcr_cassettes/organizations/teams#delete.json b/test/fixture/vcr_cassettes/organizations/teams#delete.json new file mode 100644 index 0000000..02d7d7a --- /dev/null +++ b/test/fixture/vcr_cassettes/organizations/teams#delete.json @@ -0,0 +1,41 @@ +[ + { + "request": { + "body": "\"\"", + "headers": { + "User-agent": "tentacat", + "Authorization": "token yourtokengoeshere" + }, + "method": "delete", + "options": [], + "request_body": "", + "url": "https://api.github.com/teams/2019723" + }, + "response": { + "body": "", + "headers": { + "Server": "GitHub.com", + "Date": "Thu, 12 May 2016 05:13:44 GMT", + "Status": "204 No Content", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4971", + "X-RateLimit-Reset": "1463030385", + "X-OAuth-Scopes": "admin:org", + "X-Accepted-OAuth-Scopes": "admin:org, repo", + "X-GitHub-Media-Type": "github.v3; format=json", + "Access-Control-Expose-Headers": "ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval", + "Access-Control-Allow-Origin": "*", + "Content-Security-Policy": "default-src 'none'", + "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload", + "X-Content-Type-Options": "nosniff", + "X-Frame-Options": "deny", + "X-XSS-Protection": "1; mode=block", + "Vary": "Accept-Encoding", + "X-Served-By": "a30e6f9aa7cf5731b87dfb3b9992202d", + "X-GitHub-Request-Id": "44073D0C:B976:85FB8DD:57341108" + }, + "status_code": 204, + "type": "ok" + } + } +] diff --git a/test/fixture/vcr_cassettes/organizations/teams#find.json b/test/fixture/vcr_cassettes/organizations/teams#find.json new file mode 100644 index 0000000..1a02f31 --- /dev/null +++ b/test/fixture/vcr_cassettes/organizations/teams#find.json @@ -0,0 +1,46 @@ +[ + { + "request": { + "body": "\"\\\"\\\"\"", + "headers": { + "User-agent": "tentacat", + "Authorization": "token yourtokengoeshere" + }, + "method": "get", + "options": [], + "request_body": "", + "url": "https://api.github.com/teams/1500000" + }, + "response": { + "body": "{\"name\":\"Founders\",\"id\":1500000,\"slug\":\"founders\",\"description\":null,\"permission\":\"pull\",\"privacy\":\"secret\",\"url\":\"https://api.github.com/teams/1500000\",\"members_url\":\"https://api.github.com/teams/1500000/members{/member}\",\"repositories_url\":\"https://api.github.com/teams/1500000/repos\",\"members_count\":4,\"repos_count\":2,\"organization\":{\"login\":\"my_org\",\"id\":9755201,\"url\":\"https://api.github.com/orgs/my_org\",\"repos_url\":\"https://api.github.com/orgs/my_org/repos\",\"events_url\":\"https://api.github.com/orgs/my_org/events\",\"hooks_url\":\"https://api.github.com/orgs/my_org/hooks\",\"issues_url\":\"https://api.github.com/orgs/my_org/issues\",\"members_url\":\"https://api.github.com/orgs/my_org/members{/member}\",\"public_members_url\":\"https://api.github.com/orgs/my_org/public_members{/member}\",\"avatar_url\":\"https://avatars.githubusercontent.com/u/9755201?v=3\",\"description\":\"\",\"name\":\"\",\"company\":null,\"blog\":\"\",\"location\":\"\",\"email\":\"\",\"public_repos\":2,\"public_gists\":0,\"followers\":0,\"following\":0,\"html_url\":\"https://github.com/my_org\",\"created_at\":\"2014-11-14T22:33:30Z\",\"updated_at\":\"2016-04-30T19:56:09Z\",\"type\":\"Organization\"}}", + "headers": { + "Server": "GitHub.com", + "Date": "Thu, 12 May 2016 05:06:04 GMT", + "Content-Type": "application/json; charset=utf-8", + "Content-Length": "1151", + "Status": "200 OK", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4975", + "X-RateLimit-Reset": "1463030385", + "Cache-Control": "private, max-age=60, s-maxage=60", + "Vary": "Accept, Authorization, Cookie, X-GitHub-OTP", + "ETag": "\"b7106e76a447de30ad21d800ca9b69ee\"", + "Last-Modified": "Sat, 09 Apr 2016 17:25:44 GMT", + "X-OAuth-Scopes": "admin:org", + "X-Accepted-OAuth-Scopes": "admin:org, read:org, repo, user, write:org", + "X-GitHub-Media-Type": "github.v3; format=json", + "Access-Control-Expose-Headers": "ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval", + "Access-Control-Allow-Origin": "*", + "Content-Security-Policy": "default-src 'none'", + "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload", + "X-Content-Type-Options": "nosniff", + "X-Frame-Options": "deny", + "X-XSS-Protection": "1; mode=block", + "X-Served-By": "dc1ce2bfb41810a06c705e83b388572d", + "X-GitHub-Request-Id": "44073D0C:CA0D:34631BF:57340F3C" + }, + "status_code": 200, + "type": "ok" + } + } +] diff --git a/test/fixture/vcr_cassettes/organizations/teams#list.json b/test/fixture/vcr_cassettes/organizations/teams#list.json new file mode 100644 index 0000000..febb3e0 --- /dev/null +++ b/test/fixture/vcr_cassettes/organizations/teams#list.json @@ -0,0 +1,45 @@ +[ + { + "request": { + "body": "\"\\\"\\\"\"", + "headers": { + "User-agent": "tentacat", + "Authorization": "token yourtokencomeshere" + }, + "method": "get", + "options": [], + "request_body": "", + "url": "https://api.github.com/orgs/my_org/teams" + }, + "response": { + "body": "[]", + "headers": { + "Server": "GitHub.com", + "Date": "Thu, 12 May 2016 04:56:48 GMT", + "Content-Type": "application/json; charset=utf-8", + "Content-Length": "873", + "Status": "200 OK", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4978", + "X-RateLimit-Reset": "1463030385", + "Cache-Control": "private, max-age=60, s-maxage=60", + "Vary": "Accept, Authorization, Cookie, X-GitHub-OTP", + "ETag": "\"914118fb35d5a61223401ea348786010\"", + "X-OAuth-Scopes": "admin:org", + "X-Accepted-OAuth-Scopes": "admin:org, read:org, repo, user, write:org", + "X-GitHub-Media-Type": "github.v3; format=json", + "Access-Control-Expose-Headers": "ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval", + "Access-Control-Allow-Origin": "*", + "Content-Security-Policy": "default-src 'none'", + "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload", + "X-Content-Type-Options": "nosniff", + "X-Frame-Options": "deny", + "X-XSS-Protection": "1; mode=block", + "X-Served-By": "474556b853193c38f1b14328ce2d1b7d", + "X-GitHub-Request-Id": "44073D0C:2CB6:29A0E4C:57340D10" + }, + "status_code": 200, + "type": "ok" + } + } +] diff --git a/test/fixture/vcr_cassettes/organizations/teams#update.json b/test/fixture/vcr_cassettes/organizations/teams#update.json new file mode 100644 index 0000000..76263e0 --- /dev/null +++ b/test/fixture/vcr_cassettes/organizations/teams#update.json @@ -0,0 +1,45 @@ +[ + { + "request": { + "body": "{\"name\":\"Updated Name\"}", + "headers": { + "User-agent": "tentacat", + "Authorization": "token yourtokengoeshere" + }, + "method": "patch", + "options": [], + "request_body": "", + "url": "https://api.github.com/teams/2019723" + }, + "response": { + "body": "{\"name\":\"Updated Name\",\"id\":2019723,\"slug\":\"updated-name\",\"description\":\"Team for everyone with commit access\",\"permission\":\"pull\",\"privacy\":\"closed\",\"url\":\"https://api.github.com/teams/2019723\",\"members_url\":\"https://api.github.com/teams/2019723/members{/member}\",\"repositories_url\":\"https://api.github.com/teams/2019723/repos\",\"members_count\":0,\"repos_count\":0,\"organization\":{\"login\":\"my_org\",\"id\":9755201,\"url\":\"https://api.github.com/orgs/my_org\",\"repos_url\":\"https://api.github.com/orgs/my_org/repos\",\"events_url\":\"https://api.github.com/orgs/my_org/events\",\"hooks_url\":\"https://api.github.com/orgs/my_org/hooks\",\"issues_url\":\"https://api.github.com/orgs/my_org/issues\",\"members_url\":\"https://api.github.com/orgs/my_org/members{/member}\",\"public_members_url\":\"https://api.github.com/orgs/my_org/public_members{/member}\",\"avatar_url\":\"https://avatars.githubusercontent.com/u/9755201?v=3\",\"description\":\"\",\"name\":\"\",\"company\":null,\"blog\":\"\",\"location\":\"\",\"email\":\"\",\"public_repos\":2,\"public_gists\":0,\"followers\":0,\"following\":0,\"html_url\":\"https://github.com/my_org\",\"created_at\":\"2014-11-14T22:33:30Z\",\"updated_at\":\"2016-04-30T19:56:09Z\",\"type\":\"Organization\"}}", + "headers": { + "Server": "GitHub.com", + "Date": "Thu, 12 May 2016 05:12:51 GMT", + "Content-Type": "application/json; charset=utf-8", + "Content-Length": "1193", + "Status": "200 OK", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4972", + "X-RateLimit-Reset": "1463030385", + "Cache-Control": "private, max-age=60, s-maxage=60", + "Vary": "Accept, Authorization, Cookie, X-GitHub-OTP", + "ETag": "\"79b7f3943a9f41aafa940acc39ddc463\"", + "X-OAuth-Scopes": "admin:org", + "X-Accepted-OAuth-Scopes": "admin:org, repo", + "X-GitHub-Media-Type": "github.v3; format=json", + "Access-Control-Expose-Headers": "ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval", + "Access-Control-Allow-Origin": "*", + "Content-Security-Policy": "default-src 'none'", + "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload", + "X-Content-Type-Options": "nosniff", + "X-Frame-Options": "deny", + "X-XSS-Protection": "1; mode=block", + "X-Served-By": "065b43cd9674091fec48a221b420fbb3", + "X-GitHub-Request-Id": "44073D0C:B979:B750DDA:573410D3" + }, + "status_code": 200, + "type": "ok" + } + } +] diff --git a/test/organizations/teams_test.exs b/test/organizations/teams_test.exs new file mode 100644 index 0000000..04c584d --- /dev/null +++ b/test/organizations/teams_test.exs @@ -0,0 +1,56 @@ +defmodule Tentacat.Organizations.TeamsTest do + use ExUnit.Case, async: false + use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney + import Tentacat.Organizations.Teams + + doctest Tentacat.Organizations.Teams + + @client Tentacat.Client.new(%{access_token: "8e663c8614ced27c09b963f806ac46776a29db50"}) + + setup_all do + HTTPoison.start + end + + test "list/2" do + use_cassette "organizations/teams#list" do + assert list("my_org", @client) == [] + end + end + + test "find/2" do + use_cassette "organizations/teams#find" do + %{"name" => name} = find(1500000, @client) + assert name == "Founders" + end + end + + test "create/3" do + use_cassette "organizations/teams#create" do + body = %{ + "name" => "my new team", + "description" => "Team for everyone with commit access", + "repo_names" => [ + "my_org/secret_repo", + "my_org/secret_repo2", + ], + "privacy" => "closed", + } + {status_code, _} = create("my_org", body, @client) + assert status_code == 201 + end + end + + test "update/3" do + use_cassette "organizations/teams#update" do + %{"name" => name} = update(2019723, %{name: "Updated Name"}, @client) + assert name == "Updated Name" + end + end + + test "delete/2" do + use_cassette "organizations/teams#delete" do + {status_code, _} = delete(2019723, @client) + assert status_code == 204 + end + end +end