From 839049ac5a34332bce5a916ee73065f314eb0017 Mon Sep 17 00:00:00 2001 From: mbernier Date: Mon, 27 Apr 2015 13:56:14 -0600 Subject: [PATCH 1/5] Halfway to having request headers --- plugins/apiv3_example.rb | 26 ++- plugins/converter_apiblueprint.rb | 53 ++++- .../Web_API_v3/API_Keys/index.apiblueprint | 193 ++++++++++++++++++ 3 files changed, 261 insertions(+), 11 deletions(-) create mode 100644 source/API_Reference/Web_API_v3/API_Keys/index.apiblueprint diff --git a/plugins/apiv3_example.rb b/plugins/apiv3_example.rb index d6c71474d0..d10b320fb3 100644 --- a/plugins/apiv3_example.rb +++ b/plugins/apiv3_example.rb @@ -2,6 +2,8 @@ module Jekyll class ApiV3Example < Liquid::Block def initialize(tag_name, markup, tokens) attributes = markup.split + + @identifier = attributes[0] @request_type = attributes[1] @url = attributes[2] @@ -9,9 +11,15 @@ def initialize(tag_name, markup, tokens) if attributes[4] - @show_livedoc = attributes[4] + if (attributes[4].include? "request_headers:" ) + @request_header = attributes[4].gsub("request_headers:","") + else + @show_livedoc = attributes[4] + end + end + super end @@ -33,6 +41,7 @@ def render_all(list, context) context['request_type'] = @request_type context['url'] = @url context['data'] = @data + context['request_header'] = @request_header super end end @@ -58,8 +67,11 @@ def render(context) identifier = Liquid::Template.parse("{{ identifier }}").render context request_type = Liquid::Template.parse("{{ request_type }}").render context url = Liquid::Template.parse("{{ url }}").render context + request_header = Liquid::Template.parse("{{ request_header }}").render context data = Liquid::Template.parse("{{ data }}").render context + data_block = ''; + if data.include? "=" data = Hash[URI.decode_www_form(data)] data = unquote_data(data) @@ -68,9 +80,17 @@ def render(context) data = data.length > 0 ? JSON.pretty_generate(JSON.parse(data)) : "" end + if request_header.length > 0 + request_header.gsub('{"',"").gsub('"}',"") + puts "#####DATA####" + puts "Request Headers
{% codeblock lang:json %}#{request_header}{% endcodeblock %}" + data_block = "Request Headers
{% codeblock lang:json %}#{request_header}{% endcodeblock %}" + end + if data.length > 0 - data_block = "Request Body
{% codeblock lang:json %}#{data}{% endcodeblock %}" - end + data_block += "Request Body
{% codeblock lang:json %}#{data}{% endcodeblock %}" + end + request_url = url output = < 1 + #make sure that we have the space there to be parsed + + if @@request_body.length <= 1 + url += " " + "{}" + end + + #add the request headers to the tag + url += " {\"request_headers:" + @@request_headers.strip + "\"}" + end + # output all the tags at once for this endpoint # if/when we want to put params on the URL in the Request area, we can do this append - @@params.gsub("\n","")+ - to the Url # we will need to probably grab params for the URL from the params list, rather than the request body @@ -493,7 +530,7 @@ def docs_liquid_params_block(text) def reset_vars() debug "## RESET EVERYTHING ##" # reset everything, just in case we're about to do another endpoint - path doesn't get reset until another h2, bc we reuse the path - @@body_block, @@group_identifier, @@http_response, @@params, @@request_body, @@response_body = "","","","","","","" + @@body_block, @@group_identifier, @@http_response, @@params, @@request_body, @@response_body, @@request_headers = "","","","","","","","" end end \ No newline at end of file diff --git a/source/API_Reference/Web_API_v3/API_Keys/index.apiblueprint b/source/API_Reference/Web_API_v3/API_Keys/index.apiblueprint new file mode 100644 index 0000000000..b4ecc6d366 --- /dev/null +++ b/source/API_Reference/Web_API_v3/API_Keys/index.apiblueprint @@ -0,0 +1,193 @@ +--- +layout: page +weight: 755 +title: API Keys +navigation: + show: true +--- +FORMAT: 1A + +# API Keys +The API Keys feature allows customers to be able to generate an +API Key credential which can be used for authentication with the +SendGrid v3 Web API or Mail API. + +# Group API Keys management + +## API Keys collection [/v3/api_keys] +### List all API Keys belonging to the authenticated user [GET] + ++ Response 200 (application/json) + + + Body + + { + "result": [ + { + "name": "A New Hope", + "api_key_id": "xxxxxxxx" + } + ] + } + +### Generate a new API Key for the authenticated user [POST] +This will create a new random API Key for the user. +A JSON request body containing a "name" property is required. +If number of maximum keys is reached, HTTP 403 will be returned. + ++ Request + + + Body + + { + "name":"My API Key" + } + ++ Response 201 (application/json) + + { + "api_key": "SG.xxxxxxxx.yyyyyyyy", + "api_key_id": "xxxxxxxx" + "name": "My API Key" + } + ++ Response 400 (application/json) + + + Body + + { + "errors": [ + { + "field": "name", + "message": "missing required argument" + } + ] + } + ++ Response 403 (application/json) + + + Body + + { + "errors": [ + { + "field": null, + "message": "Cannot create more than 100 API Keys" + } + ] + } + +## API Key [/api_keys/{api_key}] +### Revoke an existing API Key [DELETE] +Authentications using this API Key will fail +after this request is made, with some small propogation delay. + +If the API Key ID does not exist an HTTP 404 will be returned. + ++ Response 204 (application/json) + ++ Response 404 (application/json) + + + Body + + { + "errors": [ + { + "field": null, + "message": "unable to find API Key for deletion" + } + ] + } + +### Update the name of an existing API Key [PATCH] +A JSON request body with a "name" property is required. + ++ Request + + + Body + + { + "name": "A New Hope" + } + ++ Response 200 (application/json) + + + Body + + { + "api_key_id": "qfTQ6KG0QBiwWdJ0-pCLCA", + "name": "A New Hope" + } + ++ Response 400 (application/json) + + + Body + + { + "errors": [ + { + "field": null, + "message": "expected JSON request body with 'name' property" + } + ] + } + ++ Response 404 (application/json) + + + Body + + { + "errors": [ + { + "field": null, + "message": "unable to find API Key to update" + } + ] + } + +# Group API Key usage +The API Keys feature adds a new "Bearer" scheme for HTTP authentication +for both v3 WebAPI and the mail.send API. + +## Authentication against a /v3 API resource [/v3/resource] +Returns HTTP 401 if authentication fails. + +### Authentication [GET] ++ Request + + + Headers + + Authorization: Bearer SG.xxxxxxxx.yyyyyyyy + ++ Response 401 (application/json) + + { + "errors": [ + { + "field": null, + "message": "authorization required" + } + ] + } + + +## Authentication against the mail.send API [/api/mail.send.json] +Returns HTTP 400 if authentication fails. + +### HTTP Authentication [GET] ++ Request + + + Headers + + Authorization: Bearer SG.xxxxxxxx.yyyyyyyy + ++ Response 400 (application/json) + + + Body + + { + "message": "error", + "errors": [ + "invalid API Key" + ] + } From 22d0dd73036a276c94c896f13de378c4a0a21059 Mon Sep 17 00:00:00 2001 From: mbernier Date: Mon, 27 Apr 2015 16:17:17 -0600 Subject: [PATCH 2/5] added new functionality for parsing blueprints includes request_headers --- plugins/apiv3_example.rb | 49 +++++++++++++------ plugins/converter_apiblueprint.rb | 24 +++++++-- .../Web_API_v3/API_Keys/index.apiblueprint | 4 +- 3 files changed, 55 insertions(+), 22 deletions(-) diff --git a/plugins/apiv3_example.rb b/plugins/apiv3_example.rb index d10b320fb3..f7456d8038 100644 --- a/plugins/apiv3_example.rb +++ b/plugins/apiv3_example.rb @@ -1,24 +1,44 @@ module Jekyll class ApiV3Example < Liquid::Block def initialize(tag_name, markup, tokens) - attributes = markup.split + attributes = markup.split @identifier = attributes[0] @request_type = attributes[1] @url = attributes[2] - @data = markup.gsub(attributes[0],"").gsub(attributes[1],"").gsub(attributes[2],"").strip + #clean the original attributes from the original content + markup = markup.gsub(@identifier,"").gsub(@request_type,"").gsub(@url,"") - if attributes[4] - if (attributes[4].include? "request_headers:" ) - @request_header = attributes[4].gsub("request_headers:","") - else - @show_livedoc = attributes[4] + #get the data - which is "request data" and "request headers" if they exist + @data, @request_header = markup.scan(/\{(.*?)\}/) + + unless @data.nil? + #hack it back together now + @data = "{"+@data[0].to_s+"}" + markup = markup.gsub("{"+@data[0].to_s+"}", "") + if @data = "{}" + @data = "" end + end + + unless @request_header.nil? + @request_header = @request_header[0] + markup = markup.gsub("{"+@request_header.to_s+"}", "") + if @request_header.include? "request_header" + @request_header = @request_header.gsub("request_header","") + else + #this isn't a request header and it's out of order + @data = "{"+@request_header.to_s+"}" + @request_header = "" + end end + if markup.strip.length > 0 + @show_livedoc = markup + end super end @@ -26,7 +46,6 @@ def initialize(tag_name, markup, tokens) def render(context) output = super - output = < #{output} @@ -40,8 +59,8 @@ def render_all(list, context) context['identifier'] = @identifier context['request_type'] = @request_type context['url'] = @url - context['data'] = @data - context['request_header'] = @request_header + context['data'] = @data.to_s + context['request_header'] = @request_header.to_s super end end @@ -62,7 +81,7 @@ def unquote_data(data) def render(context) output = super - + # let's parse the vars before we generate the output to avoid complications identifier = Liquid::Template.parse("{{ identifier }}").render context request_type = Liquid::Template.parse("{{ request_type }}").render context @@ -81,10 +100,8 @@ def render(context) end if request_header.length > 0 - request_header.gsub('{"',"").gsub('"}',"") - puts "#####DATA####" - puts "Request Headers
{% codeblock lang:json %}#{request_header}{% endcodeblock %}" - data_block = "Request Headers
{% codeblock lang:json %}#{request_header}{% endcodeblock %}" + request_header = request_header.gsub('-_-',"") + data_block = "Request Headers
{% codeblock %}#{request_header}{% endcodeblock %}" end if data.length > 0 @@ -102,7 +119,7 @@ def render(context) {% codeblock lang:json %}#{output.strip}{% endcodeblock %} HTML - + return Liquid::Template.parse(output).render context end end diff --git a/plugins/converter_apiblueprint.rb b/plugins/converter_apiblueprint.rb index 9a91c1c52d..52aede6965 100644 --- a/plugins/converter_apiblueprint.rb +++ b/plugins/converter_apiblueprint.rb @@ -142,7 +142,7 @@ def header(text, level, somethingelse = nil) # get the response method from the header @@method = text.split("[")[1].gsub("]","") else - puts "HEADER parse error - no HTTP method on " + text + debug "HEADER parse error - no HTTP method on " + text exit end @@ -208,6 +208,8 @@ def list_item(text, list_type) # When we have headers that tag along, we need to indicate this if text.include? "Headers" + debug "HEADERS!! " + text + return docs_header(text) end @@ -306,9 +308,13 @@ def docs_header(text) text = text.gsub("Headers","") + debug "In docs_header body_block = " + @@body_block + if "request" == @@body_block debug "Found a header! " + text + @@request_headers = text.gsub("\n","").gsub(""",'"') + end if "response" == @@body_block @@ -328,7 +334,7 @@ def docs_body(text) @@request_body = text.gsub("\n","").gsub(""",'"') - puts "\t REQUEST BODY SET " + text.strip + debug "\t REQUEST BODY SET " + text.strip # we don't want this to be put out to the page yet return @@ -460,15 +466,25 @@ def docs_liquid_output(text) url += " " + @@request_body.strip end + debug "REQUEST HEADERS: " + @@request_headers + if @@request_headers.length > 1 + + debug "WE HAVE REQUEST HEADERS" + #make sure that we have the space there to be parsed if @@request_body.length <= 1 - url += " " + "{}" + debug "REquest Body is 0" + url += " {}" end + debug "adding request headers to URL" + #add the request headers to the tag - url += " {\"request_headers:" + @@request_headers.strip + "\"}" + url += " {request_header" + @@request_headers.strip + "}" + + debug "####URL####" + url end # output all the tags at once for this endpoint diff --git a/source/API_Reference/Web_API_v3/API_Keys/index.apiblueprint b/source/API_Reference/Web_API_v3/API_Keys/index.apiblueprint index b4ecc6d366..c4d15df450 100644 --- a/source/API_Reference/Web_API_v3/API_Keys/index.apiblueprint +++ b/source/API_Reference/Web_API_v3/API_Keys/index.apiblueprint @@ -14,7 +14,7 @@ SendGrid v3 Web API or Mail API. # Group API Keys management -## API Keys collection [/v3/api_keys] +## API Keys collection [/api_keys] ### List all API Keys belonging to the authenticated user [GET] + Response 200 (application/json) @@ -149,7 +149,7 @@ A JSON request body with a "name" property is required. The API Keys feature adds a new "Bearer" scheme for HTTP authentication for both v3 WebAPI and the mail.send API. -## Authentication against a /v3 API resource [/v3/resource] +## Authentication against a /v3 API resource [/resource] Returns HTTP 401 if authentication fails. ### Authentication [GET] From 5e6aa5ed07eab78a4bb90b2b09a65c9a6676ab92 Mon Sep 17 00:00:00 2001 From: mbernier Date: Mon, 27 Apr 2015 16:22:30 -0600 Subject: [PATCH 3/5] updated the blueprint file --- .../API_Reference/Web_API_v3/API_Keys/index.apiblueprint | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/API_Reference/Web_API_v3/API_Keys/index.apiblueprint b/source/API_Reference/Web_API_v3/API_Keys/index.apiblueprint index c4d15df450..d472f3b52d 100644 --- a/source/API_Reference/Web_API_v3/API_Keys/index.apiblueprint +++ b/source/API_Reference/Web_API_v3/API_Keys/index.apiblueprint @@ -5,6 +5,7 @@ title: API Keys navigation: show: true --- + FORMAT: 1A # API Keys @@ -159,6 +160,8 @@ Returns HTTP 401 if authentication fails. Authorization: Bearer SG.xxxxxxxx.yyyyyyyy ++ Response 200 (application/json) + + Response 401 (application/json) { @@ -181,6 +184,8 @@ Returns HTTP 400 if authentication fails. Authorization: Bearer SG.xxxxxxxx.yyyyyyyy ++ Response 200 (application/json) + + Response 400 (application/json) + Body @@ -190,4 +195,4 @@ Returns HTTP 400 if authentication fails. "errors": [ "invalid API Key" ] - } + } \ No newline at end of file From 69d9c46bcd7b71e8575239b4f1cef1499d1ca82f Mon Sep 17 00:00:00 2001 From: mbernier Date: Mon, 27 Apr 2015 17:01:52 -0600 Subject: [PATCH 4/5] Removed HTML comment for the API Keys API link --- source/User_Guide/Account/api_keys.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/User_Guide/Account/api_keys.md b/source/User_Guide/Account/api_keys.md index 8aa3730d18..cae1f18971 100644 --- a/source/User_Guide/Account/api_keys.md +++ b/source/User_Guide/Account/api_keys.md @@ -26,7 +26,7 @@ Permissions that allow you to define what a given API Key is allowed to do are i Managing API Keys {%endanchor%} -You can [manage your API Keys](https://sendgrid.com/beta/settings/api_keys) from our beta interface. +You can [manage your API Keys](https://sendgrid.com/beta/settings/api_keys) from our beta interface. Additionally, you can [manage your API keys via the API itself]({{root_url}}/API_Reference/Web_API_v3/API_Keys/index.html)). {% anchor h2%} Using API Keys From d0256f14e15b64de99b79a3357ff363017bfb4d4 Mon Sep 17 00:00:00 2001 From: Brandon West Date: Mon, 27 Apr 2015 18:28:38 -0600 Subject: [PATCH 5/5] Update password requirements --- source/API_Reference/Customer_Subuser_API/subusers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/API_Reference/Customer_Subuser_API/subusers.md b/source/API_Reference/Customer_Subuser_API/subusers.md index 1fd82ab0ad..d532052bb9 100644 --- a/source/API_Reference/Customer_Subuser_API/subusers.md +++ b/source/API_Reference/Customer_Subuser_API/subusers.md @@ -22,7 +22,7 @@ Create a Subuser {% parameters create %} {% parameter username Yes 'No more than 64 characters' 'New subuser username' %} - {% parameter password Yes 'At least 6 characters' 'New subuser password' %} + {% parameter password Yes 'At least 8 characters' 'New subuser password' %} {% parameter confirm_password Yes 'Match password argument' 'Confirm new password' %} {% parameter email Yes 'Valid email address, no more than 64 characters' 'New subuser email. This is an administrative contact address for SendGrid' %} {% parameter first_name Yes 'No more than 50 characters' 'New subuser profile first name' %}