pact_broker 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +13 -5
  2. data/.gitignore +2 -0
  3. data/CHANGELOG.md +5 -0
  4. data/README.md +2 -1
  5. data/Rakefile +1 -3
  6. data/db/migrations/07_increase_json_content_length.rb +6 -1
  7. data/db/migrations/08_create_latest_pact_view.rb +7 -5
  8. data/db/migrations/14_add_timestamps_to_pact_views.rb +28 -0
  9. data/lib/pact_broker/api.rb +0 -4
  10. data/lib/pact_broker/api/decorators/latest_pact_decorator.rb +4 -1
  11. data/lib/pact_broker/api/decorators/pact_decorator.rb +2 -2
  12. data/lib/pact_broker/api/decorators/pact_version_decorator.rb +3 -4
  13. data/lib/pact_broker/api/decorators/pact_versions_decorator.rb +4 -4
  14. data/lib/pact_broker/api/decorators/pacticipant_decorator.rb +3 -2
  15. data/lib/pact_broker/api/decorators/tag_decorator.rb +2 -2
  16. data/lib/pact_broker/api/decorators/timestamps.rb +24 -0
  17. data/lib/pact_broker/api/decorators/webhook_decorator.rb +2 -2
  18. data/lib/pact_broker/api/decorators/webhooks_decorator.rb +4 -4
  19. data/lib/pact_broker/api/renderers/html_pact_renderer.rb +16 -4
  20. data/lib/pact_broker/api/resources/base_resource.rb +0 -1
  21. data/lib/pact_broker/api/resources/group.rb +11 -6
  22. data/lib/pact_broker/api/resources/index.rb +5 -6
  23. data/lib/pact_broker/api/resources/latest_pact.rb +8 -5
  24. data/lib/pact_broker/api/resources/latest_pacts.rb +3 -3
  25. data/lib/pact_broker/api/resources/pact.rb +9 -5
  26. data/lib/pact_broker/api/resources/pact_webhooks.rb +9 -5
  27. data/lib/pact_broker/api/resources/pacticipant.rb +10 -5
  28. data/lib/pact_broker/api/resources/relationships.rb +3 -3
  29. data/lib/pact_broker/api/resources/tag.rb +8 -7
  30. data/lib/pact_broker/api/resources/webhook.rb +1 -1
  31. data/lib/pact_broker/api/resources/webhook_execution.rb +1 -1
  32. data/lib/pact_broker/app.rb +8 -57
  33. data/lib/pact_broker/logging.rb +1 -0
  34. data/lib/pact_broker/models/pact.rb +6 -8
  35. data/lib/pact_broker/repositories/pact.rb +29 -0
  36. data/lib/pact_broker/repositories/pact_content.rb +0 -0
  37. data/lib/pact_broker/repositories/pact_repository.rb +66 -21
  38. data/lib/pact_broker/repositories/tag_repository.rb +1 -1
  39. data/lib/pact_broker/repositories/version_repository.rb +2 -1
  40. data/lib/pact_broker/services/pact_service.rb +14 -16
  41. data/lib/pact_broker/ui.rb +2 -0
  42. data/lib/pact_broker/ui/app.rb +81 -0
  43. data/lib/pact_broker/ui/controllers/groups.rb +1 -1
  44. data/lib/pact_broker/version.rb +1 -1
  45. data/lib/rack/pact_broker/convert_file_extension_to_accept_header.rb +44 -0
  46. data/pact_broker.gemspec +1 -0
  47. data/public/javascripts/highlight.pack.js +1 -0
  48. data/public/stylesheets/github-json.css +127 -0
  49. data/public/stylesheets/pact.css +4 -0
  50. data/spec/fixtures/consumer-provider.json +21 -0
  51. data/spec/integration/app_spec.rb +165 -0
  52. data/spec/integration/endpoints/pact_put_spec.rb +43 -0
  53. data/spec/lib/pact_broker/api/decorators/latest_pact_decorator_spec.rb +42 -0
  54. data/spec/lib/pact_broker/api/decorators/pact_version_decorator_spec.rb +5 -0
  55. data/spec/lib/pact_broker/api/decorators/pacticipant_decorator_spec.rb +2 -1
  56. data/spec/lib/pact_broker/api/decorators/webhook_decorator_spec.rb +9 -2
  57. data/spec/lib/pact_broker/api/renderers/html_pact_renderer_spec.rb +14 -3
  58. data/spec/lib/pact_broker/repositories/pact_repository_spec.rb +88 -4
  59. data/spec/lib/pact_broker/repositories/tag_repository_spec.rb +2 -0
  60. data/spec/lib/pact_broker/repositories/version_repository_spec.rb +20 -2
  61. data/spec/lib/pact_broker/services/pacticipant_service_spec.rb +1 -1
  62. data/spec/service_consumers/pact_helper.rb +2 -17
  63. data/spec/spec_helper.rb +4 -22
  64. data/spec/support/database_cleaner.rb +18 -0
  65. data/spec/support/fixture_helpers.rb +10 -0
  66. data/spec/support/provider_state_builder.rb +4 -15
  67. metadata +82 -47
@@ -11,7 +11,7 @@ module PactBroker
11
11
 
12
12
  def find args
13
13
  PactBroker::Models::Tag
14
- .select(:tags__name, :tags__version_id)
14
+ .select(:tags__name, :tags__version_id, :tags__created_at, :tags__updated_at)
15
15
  .join(:versions, {id: :version_id})
16
16
  .join(:pacticipants, {pacticipants__id: :versions__pacticipant_id})
17
17
  .where(:tags__name => args.fetch(:tag_name))
@@ -19,7 +19,8 @@ module PactBroker
19
19
 
20
20
  def create args
21
21
  PactBroker.logger.info "Creating version #{args[:number]} for pacticipant_id=#{args[:pacticipant_id]}"
22
- PactBroker::Models::Version.new(number: args[:number], pacticipant_id: args[:pacticipant_id]).save
22
+ version = PactBroker::Models::Version.new(number: args[:number], pacticipant_id: args[:pacticipant_id]).save
23
+ PactBroker::Models::Version.find(id: version.id) # Need to reload with populated order
23
24
  end
24
25
 
25
26
  def find_by_pacticipant_id_and_number_or_create pacticipant_id, number
@@ -26,14 +26,13 @@ module PactBroker
26
26
  provider = pacticipant_repository.find_by_name_or_create params[:provider_name]
27
27
  consumer = pacticipant_repository.find_by_name_or_create params[:consumer_name]
28
28
  consumer_version = version_repository.find_by_pacticipant_id_and_number_or_create consumer.id, params[:consumer_version_number]
29
- pact = pact_repository.find_by_version_and_provider(consumer_version.id, provider.id)
29
+ existing_pact = pact_repository.find_by_version_and_provider(consumer_version.id, provider.id)
30
30
 
31
- if pact
32
- return update_pact params, pact
31
+ if existing_pact
32
+ update_pact params, existing_pact
33
33
  else
34
- return create_pact params, consumer_version, provider
34
+ create_pact params, consumer_version, provider
35
35
  end
36
-
37
36
  end
38
37
 
39
38
  def find_all_pacts_between consumer, options
@@ -63,24 +62,23 @@ module PactBroker
63
62
 
64
63
  private
65
64
 
66
- def update_pact params, pact
67
- previous_json_content = pact.json_content
68
- pact.update(json_content: params[:json_content])
69
- if previous_json_content != params[:json_content]
70
- webhook_service.execute_webhooks pact
65
+ def update_pact params, existing_pact
66
+ updated_pact = pact_repository.update existing_pact.id, params
67
+
68
+ if existing_pact.json_content != updated_pact.json_content
69
+ webhook_service.execute_webhooks updated_pact
71
70
  end
72
- return pact, false
71
+
72
+ updated_pact
73
73
  end
74
74
 
75
75
  def create_pact params, version, provider
76
76
  pact = pact_repository.create json_content: params[:json_content], version_id: version.id, provider_id: provider.id
77
- execute_webhooks pact
78
- return pact, true
77
+ trigger_webhooks pact
78
+ pact
79
79
  end
80
80
 
81
-
82
-
83
- def execute_webhooks pact
81
+ def trigger_webhooks pact
84
82
  if pact_has_changed_since_previous_version? pact
85
83
  webhook_service.execute_webhooks pact
86
84
  end
@@ -0,0 +1,2 @@
1
+ require 'pact_broker/ui/controllers/relationships'
2
+ require 'pact_broker/ui/controllers/groups'
@@ -0,0 +1,81 @@
1
+ require 'pact_broker/ui'
2
+ require 'pact_broker/doc/controllers/app'
3
+
4
+ module PactBroker
5
+ module UI
6
+ class App
7
+
8
+ def initialize
9
+ @app = ::Rack::Builder.new {
10
+
11
+ use HtmlFilter
12
+
13
+ map "/ui/relationships" do
14
+ run PactBroker::UI::Controllers::Relationships
15
+ end
16
+
17
+ map "/groups" do
18
+ run PactBroker::UI::Controllers::Groups
19
+ end
20
+
21
+ map "/doc" do
22
+ run PactBroker::Doc::Controllers::App
23
+ end
24
+
25
+ map "/" do
26
+ run RedirectRootToRelationships
27
+ end
28
+ }
29
+ end
30
+
31
+ def call env
32
+ @app.call(env)
33
+ end
34
+
35
+ class RedirectRootToRelationships
36
+
37
+ def self.call env
38
+ # A request for the root path in the browser (not the json index) should
39
+ # redirect to ui/relationships
40
+ if (env['PATH_INFO'].chomp("/") == "")
41
+ [303, {'Location' => 'ui/relationships'},[]]
42
+ else
43
+ [404, {},[]]
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ class HtmlFilter
50
+
51
+ def initialize app
52
+ @app = app
53
+ end
54
+
55
+ def call env
56
+ if accepts_html_and_not_json_or_csv env
57
+ @app.call(env)
58
+ else
59
+ [404, {},[]]
60
+ end
61
+ end
62
+
63
+ def accepts_html_and_not_json_or_csv env
64
+ accept = env['HTTP_ACCEPT'] || ''
65
+ accepts_html(accept) && !accepts_json_or_csv(accept)
66
+ end
67
+
68
+ def accepts_html(accept)
69
+ accept.include?("html")
70
+ end
71
+
72
+ def accepts_json_or_csv accept
73
+ accept.include?("json") || accept.include?("csv")
74
+ end
75
+
76
+ end
77
+
78
+ end
79
+ end
80
+
81
+ end
@@ -12,7 +12,7 @@ module PactBroker
12
12
  get ":name" do
13
13
  erb :'groups/show.html', {
14
14
  locals: {
15
- csv_path: "/groups/#{params[:name]}/csv",
15
+ csv_path: "/groups/#{params[:name]}.csv",
16
16
  pacticipant_name: params[:name]}
17
17
  }, {
18
18
  layout: 'layouts/main'
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = '1.2.0'
2
+ VERSION = '1.3.0'
3
3
  end
@@ -0,0 +1,44 @@
1
+ module Rack
2
+ module PactBroker
3
+
4
+ # If the HTML and the CSV group resources are both requested by the browser,
5
+ # Chrome gets confused by the content types, and when you click back, it tries to load the CSV
6
+ # instead of the HTML page. So we have to give the CSV resource a different URL (.csv)
7
+
8
+ class ConvertFileExtensionToAcceptHeader
9
+
10
+ EXTENSIONS = {".csv" => "text/csv"}
11
+ EXTENSION_REGEXP = /\.\w+$/
12
+
13
+ def initialize app
14
+ @app = app
15
+ end
16
+
17
+ def call env
18
+ file_extension = extension(env)
19
+ if convert_to_accept_header? file_extension
20
+ @app.call(set_accept_header_and_path_info(env, file_extension))
21
+ else
22
+ @app.call(env)
23
+ end
24
+ end
25
+
26
+ def convert_to_accept_header? file_extension
27
+ EXTENSIONS[file_extension]
28
+ end
29
+
30
+ def extension env
31
+ env["PATH_INFO"] =~ EXTENSION_REGEXP && $~[0]
32
+ end
33
+
34
+ def set_accept_header_and_path_info env, file_extension
35
+ env.merge(
36
+ "PATH_INFO" => env["PATH_INFO"].gsub(EXTENSION_REGEXP, ''),
37
+ "HTTP_ACCEPT" => EXTENSIONS[file_extension]
38
+ )
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+ end
data/pact_broker.gemspec CHANGED
@@ -40,5 +40,6 @@ Gem::Specification.new do |gem|
40
40
  gem.add_development_dependency 'webmock'
41
41
  gem.add_development_dependency 'rspec', '~>3.0'
42
42
  gem.add_development_dependency 'rspec-its'
43
+ gem.add_development_dependency 'database_cleaner'
43
44
 
44
45
  end
@@ -0,0 +1 @@
1
+ var hljs=new function(){function j(v){return v.replace(/&/gm,"&amp;").replace(/</gm,"&lt;").replace(/>/gm,"&gt;")}function t(v){return v.nodeName.toLowerCase()}function h(w,x){var v=w&&w.exec(x);return v&&v.index==0}function r(w){var v=(w.className+" "+(w.parentNode?w.parentNode.className:"")).split(/\s+/);v=v.map(function(x){return x.replace(/^lang(uage)?-/,"")});return v.filter(function(x){return i(x)||/no(-?)highlight/.test(x)})[0]}function o(x,y){var v={};for(var w in x){v[w]=x[w]}if(y){for(var w in y){v[w]=y[w]}}return v}function u(x){var v=[];(function w(y,z){for(var A=y.firstChild;A;A=A.nextSibling){if(A.nodeType==3){z+=A.nodeValue.length}else{if(A.nodeType==1){v.push({event:"start",offset:z,node:A});z=w(A,z);if(!t(A).match(/br|hr|img|input/)){v.push({event:"stop",offset:z,node:A})}}}}return z})(x,0);return v}function q(w,y,C){var x=0;var F="";var z=[];function B(){if(!w.length||!y.length){return w.length?w:y}if(w[0].offset!=y[0].offset){return(w[0].offset<y[0].offset)?w:y}return y[0].event=="start"?w:y}function A(H){function G(I){return" "+I.nodeName+'="'+j(I.value)+'"'}F+="<"+t(H)+Array.prototype.map.call(H.attributes,G).join("")+">"}function E(G){F+="</"+t(G)+">"}function v(G){(G.event=="start"?A:E)(G.node)}while(w.length||y.length){var D=B();F+=j(C.substr(x,D[0].offset-x));x=D[0].offset;if(D==w){z.reverse().forEach(E);do{v(D.splice(0,1)[0]);D=B()}while(D==w&&D.length&&D[0].offset==x);z.reverse().forEach(A)}else{if(D[0].event=="start"){z.push(D[0].node)}else{z.pop()}v(D.splice(0,1)[0])}}return F+j(C.substr(x))}function m(y){function v(z){return(z&&z.source)||z}function w(A,z){return RegExp(v(A),"m"+(y.cI?"i":"")+(z?"g":""))}function x(D,C){if(D.compiled){return}D.compiled=true;D.k=D.k||D.bK;if(D.k){var z={};var E=function(G,F){if(y.cI){F=F.toLowerCase()}F.split(" ").forEach(function(H){var I=H.split("|");z[I[0]]=[G,I[1]?Number(I[1]):1]})};if(typeof D.k=="string"){E("keyword",D.k)}else{Object.keys(D.k).forEach(function(F){E(F,D.k[F])})}D.k=z}D.lR=w(D.l||/\b[A-Za-z0-9_]+\b/,true);if(C){if(D.bK){D.b="\\b("+D.bK.split(" ").join("|")+")\\b"}if(!D.b){D.b=/\B|\b/}D.bR=w(D.b);if(!D.e&&!D.eW){D.e=/\B|\b/}if(D.e){D.eR=w(D.e)}D.tE=v(D.e)||"";if(D.eW&&C.tE){D.tE+=(D.e?"|":"")+C.tE}}if(D.i){D.iR=w(D.i)}if(D.r===undefined){D.r=1}if(!D.c){D.c=[]}var B=[];D.c.forEach(function(F){if(F.v){F.v.forEach(function(G){B.push(o(F,G))})}else{B.push(F=="self"?D:F)}});D.c=B;D.c.forEach(function(F){x(F,D)});if(D.starts){x(D.starts,C)}var A=D.c.map(function(F){return F.bK?"\\.?("+F.b+")\\.?":F.b}).concat([D.tE,D.i]).map(v).filter(Boolean);D.t=A.length?w(A.join("|"),true):{exec:function(F){return null}}}x(y)}function c(T,L,J,R){function v(V,W){for(var U=0;U<W.c.length;U++){if(h(W.c[U].bR,V)){return W.c[U]}}}function z(V,U){if(h(V.eR,U)){return V}if(V.eW){return z(V.parent,U)}}function A(U,V){return !J&&h(V.iR,U)}function E(W,U){var V=M.cI?U[0].toLowerCase():U[0];return W.k.hasOwnProperty(V)&&W.k[V]}function w(aa,Y,X,W){var U=W?"":b.classPrefix,V='<span class="'+U,Z=X?"":"</span>";V+=aa+'">';return V+Y+Z}function N(){if(!I.k){return j(C)}var U="";var X=0;I.lR.lastIndex=0;var V=I.lR.exec(C);while(V){U+=j(C.substr(X,V.index-X));var W=E(I,V);if(W){H+=W[1];U+=w(W[0],j(V[0]))}else{U+=j(V[0])}X=I.lR.lastIndex;V=I.lR.exec(C)}return U+j(C.substr(X))}function F(){if(I.sL&&!f[I.sL]){return j(C)}var U=I.sL?c(I.sL,C,true,S):e(C);if(I.r>0){H+=U.r}if(I.subLanguageMode=="continuous"){S=U.top}return w(U.language,U.value,false,true)}function Q(){return I.sL!==undefined?F():N()}function P(W,V){var U=W.cN?w(W.cN,"",true):"";if(W.rB){D+=U;C=""}else{if(W.eB){D+=j(V)+U;C=""}else{D+=U;C=V}}I=Object.create(W,{parent:{value:I}})}function G(U,Y){C+=U;if(Y===undefined){D+=Q();return 0}var W=v(Y,I);if(W){D+=Q();P(W,Y);return W.rB?0:Y.length}var X=z(I,Y);if(X){var V=I;if(!(V.rE||V.eE)){C+=Y}D+=Q();do{if(I.cN){D+="</span>"}H+=I.r;I=I.parent}while(I!=X.parent);if(V.eE){D+=j(Y)}C="";if(X.starts){P(X.starts,"")}return V.rE?0:Y.length}if(A(Y,I)){throw new Error('Illegal lexeme "'+Y+'" for mode "'+(I.cN||"<unnamed>")+'"')}C+=Y;return Y.length||1}var M=i(T);if(!M){throw new Error('Unknown language: "'+T+'"')}m(M);var I=R||M;var S;var D="";for(var K=I;K!=M;K=K.parent){if(K.cN){D=w(K.cN,"",true)+D}}var C="";var H=0;try{var B,y,x=0;while(true){I.t.lastIndex=x;B=I.t.exec(L);if(!B){break}y=G(L.substr(x,B.index-x),B[0]);x=B.index+y}G(L.substr(x));for(var K=I;K.parent;K=K.parent){if(K.cN){D+="</span>"}}return{r:H,value:D,language:T,top:I}}catch(O){if(O.message.indexOf("Illegal")!=-1){return{r:0,value:j(L)}}else{throw O}}}function e(y,x){x=x||b.languages||Object.keys(f);var v={r:0,value:j(y)};var w=v;x.forEach(function(z){if(!i(z)){return}var A=c(z,y,false);A.language=z;if(A.r>w.r){w=A}if(A.r>v.r){w=v;v=A}});if(w.language){v.second_best=w}return v}function g(v){if(b.tabReplace){v=v.replace(/^((<[^>]+>|\t)+)/gm,function(w,z,y,x){return z.replace(/\t/g,b.tabReplace)})}if(b.useBR){v=v.replace(/\n/g,"<br>")}return v}function p(A){var B=r(A);if(/no(-?)highlight/.test(B)){return}var y;if(b.useBR){y=document.createElementNS("http://www.w3.org/1999/xhtml","div");y.innerHTML=A.innerHTML.replace(/\n/g,"").replace(/<br[ \/]*>/g,"\n")}else{y=A}var z=y.textContent;var v=B?c(B,z,true):e(z);var x=u(y);if(x.length){var w=document.createElementNS("http://www.w3.org/1999/xhtml","div");w.innerHTML=v.value;v.value=q(x,u(w),z)}v.value=g(v.value);A.innerHTML=v.value;A.className+=" hljs "+(!B&&v.language||"");A.result={language:v.language,re:v.r};if(v.second_best){A.second_best={language:v.second_best.language,re:v.second_best.r}}}var b={classPrefix:"hljs-",tabReplace:null,useBR:false,languages:undefined};function s(v){b=o(b,v)}function l(){if(l.called){return}l.called=true;var v=document.querySelectorAll("pre code");Array.prototype.forEach.call(v,p)}function a(){addEventListener("DOMContentLoaded",l,false);addEventListener("load",l,false)}var f={};var n={};function d(v,x){var w=f[v]=x(this);if(w.aliases){w.aliases.forEach(function(y){n[y]=v})}}function k(){return Object.keys(f)}function i(v){return f[v]||f[n[v]]}this.highlight=c;this.highlightAuto=e;this.fixMarkup=g;this.highlightBlock=p;this.configure=s;this.initHighlighting=l;this.initHighlightingOnLoad=a;this.registerLanguage=d;this.listLanguages=k;this.getLanguage=i;this.inherit=o;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE]};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE]};this.PWM={b:/\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such)\b/};this.CLCM={cN:"comment",b:"//",e:"$",c:[this.PWM]};this.CBCM={cN:"comment",b:"/\\*",e:"\\*/",c:[this.PWM]};this.HCM={cN:"comment",b:"#",e:"$",c:[this.PWM]};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.CSSNM={cN:"number",b:this.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0};this.RM={cN:"regexp",b:/\//,e:/\/[gim]*/,i:/\n/,c:[this.BE,{b:/\[/,e:/\]/,r:0,c:[this.BE]}]};this.TM={cN:"title",b:this.IR,r:0};this.UTM={cN:"title",b:this.UIR,r:0}}();hljs.registerLanguage("json",function(a){var e={literal:"true false null"};var d=[a.QSM,a.CNM];var c={cN:"value",e:",",eW:true,eE:true,c:d,k:e};var b={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:true,eE:true,c:[a.BE],i:"\\n",starts:c}],i:"\\S"};var f={b:"\\[",e:"\\]",c:[a.inherit(c,{cN:null})],i:"\\S"};d.splice(d.length,0,b,f);return{c:d,k:e,i:"\\S"}});
@@ -0,0 +1,127 @@
1
+ /*
2
+
3
+ github.com style (c) Vasily Polovnyov <vast@whiteants.net>
4
+
5
+ */
6
+
7
+ .hljs {
8
+ display: block;
9
+ overflow-x: auto;
10
+ padding: 0.5em;
11
+ color: #333;
12
+ background: #f8f8f8;
13
+ -webkit-text-size-adjust: none;
14
+ }
15
+
16
+ .hljs-comment,
17
+ .hljs-template_comment,
18
+ .diff .hljs-header,
19
+ .hljs-javadoc {
20
+ color: #998;
21
+ font-style: italic;
22
+ }
23
+
24
+ .hljs-keyword,
25
+ .css .rule .hljs-keyword,
26
+ .hljs-winutils,
27
+ .javascript .hljs-title,
28
+ .nginx .hljs-title,
29
+ .hljs-subst,
30
+ .hljs-request,
31
+ .hljs-status {
32
+ color: #333;
33
+ font-weight: bold;
34
+ }
35
+
36
+ .hljs-number,
37
+ .hljs-hexcolor,
38
+ .ruby .hljs-constant {
39
+ color: #008080;
40
+ }
41
+
42
+ .hljs-string,
43
+ .hljs-tag .hljs-value,
44
+ .hljs-phpdoc,
45
+ .hljs-dartdoc,
46
+ .tex .hljs-formula {
47
+ color: #d14;
48
+ }
49
+
50
+ .hljs-title,
51
+ .hljs-id,
52
+ .scss .hljs-preprocessor {
53
+ color: #900;
54
+ font-weight: bold;
55
+ }
56
+
57
+ .javascript .hljs-title,
58
+ .hljs-list .hljs-keyword,
59
+ .hljs-subst {
60
+ font-weight: normal;
61
+ }
62
+
63
+ .hljs-class .hljs-title,
64
+ .hljs-type,
65
+ .vhdl .hljs-literal,
66
+ .tex .hljs-command {
67
+ color: #458;
68
+ font-weight: bold;
69
+ }
70
+
71
+ .hljs-tag,
72
+ .hljs-tag .hljs-title,
73
+ .hljs-rules .hljs-property,
74
+ .django .hljs-tag .hljs-keyword {
75
+ color: #000080;
76
+ font-weight: normal;
77
+ }
78
+
79
+ .hljs-attribute,
80
+ .hljs-variable,
81
+ .lisp .hljs-body {
82
+ color: #000080;
83
+ }
84
+
85
+ .hljs-regexp {
86
+ color: #009926;
87
+ }
88
+
89
+ .hljs-symbol,
90
+ .ruby .hljs-symbol .hljs-string,
91
+ .lisp .hljs-keyword,
92
+ .clojure .hljs-keyword,
93
+ .scheme .hljs-keyword,
94
+ .tex .hljs-special,
95
+ .hljs-prompt {
96
+ color: #990073;
97
+ }
98
+
99
+ .hljs-built_in {
100
+ color: #0086b3;
101
+ }
102
+
103
+ .hljs-preprocessor,
104
+ .hljs-pragma,
105
+ .hljs-pi,
106
+ .hljs-doctype,
107
+ .hljs-shebang,
108
+ .hljs-cdata {
109
+ color: #999;
110
+ font-weight: bold;
111
+ }
112
+
113
+ .hljs-deletion {
114
+ background: #fdd;
115
+ }
116
+
117
+ .hljs-addition {
118
+ background: #dfd;
119
+ }
120
+
121
+ .diff .hljs-change {
122
+ background: #0086b3;
123
+ }
124
+
125
+ .hljs-chunk {
126
+ color: #aaa;
127
+ }
@@ -9,4 +9,8 @@ div.pact-metadata li {
9
9
 
10
10
  div.pact-metadata span.name {
11
11
  font-weight: bold ;
12
+ }
13
+
14
+ pre {
15
+ font-size: 1em
12
16
  }
@@ -0,0 +1,21 @@
1
+ {
2
+ "consumer": {
3
+ "name": "A Consumer"
4
+ },
5
+ "provider": {
6
+ "name": "A Provider"
7
+ },
8
+ "interactions": [
9
+ {
10
+ "request": {
11
+ "description" : "a request for something",
12
+ "method": "get",
13
+ "path" : "/something"
14
+ },
15
+ "response": {
16
+ "status": 200,
17
+ "body" : "something"
18
+ }
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,165 @@
1
+ require 'spec_helper'
2
+ require 'pact_broker/app'
3
+ require 'db'
4
+
5
+ module PactBroker
6
+
7
+ describe App do
8
+
9
+ before do
10
+ ProviderStateBuilder.new.create_pact_with_hierarchy 'Some Consumer', '1.0', 'Some Provider'
11
+ end
12
+
13
+ let(:hal_browser_enabled) { true }
14
+ let(:group_url) { '/groups/Some%20Consumer' }
15
+
16
+ let(:app) do
17
+ app = PactBroker::App.new do | config |
18
+ config.auto_migrate_db = false
19
+ config.use_hal_browser = hal_browser_enabled
20
+ config.database_connection = ::DB::PACT_BROKER_DB
21
+ end
22
+
23
+ end
24
+
25
+ context "when Accept includes text/html" do
26
+ let(:env) { {'HTTP_ACCEPT' => 'text/html'} }
27
+
28
+ subject { get path, '', env; last_response }
29
+
30
+ describe "a request for root" do
31
+
32
+ let(:path) { '/' }
33
+
34
+ it "redirects to /ui/relationships" do
35
+ expect(subject.status).to eq 303
36
+ expect(subject.headers['Location']).to eq 'ui/relationships'
37
+ end
38
+
39
+ end
40
+
41
+ describe "a request for /ui/relationships" do
42
+ let(:path) { '/ui/relationships/' }
43
+
44
+ it "returns the relationships page" do
45
+ expect(subject.status).to eq 200
46
+ expect(subject.headers['Content-Type']).to include 'text/html'
47
+ expect(subject.body).to include 'Relationships'
48
+ end
49
+ end
50
+
51
+ describe "a request for /groups/:pacticipant_name" do
52
+ let(:path) { '/groups/Some%20Consumer' }
53
+
54
+ it "returns the group page" do
55
+ expect(subject.status).to eq 200
56
+ expect(subject.headers['Content-Type']).to include 'text/html'
57
+ expect(subject.body).to include 'Network graph'
58
+ expect(subject.body).to include 'Some Consumer'
59
+ end
60
+ end
61
+
62
+ describe "a request for /doc/:rel_name" do
63
+ let(:path) { '/doc/webhooks' }
64
+
65
+ it "returns the HAL docs for the given rel" do
66
+ expect(subject.status).to eq 200
67
+ expect(subject.headers['Content-Type']).to include 'text/html'
68
+ expect(subject.body).to include 'Webhooks'
69
+ end
70
+ end
71
+
72
+ describe "a request the group containing a pacticipant" do
73
+ let(:path) { group_url }
74
+
75
+ it "returns the HTML representation of the group" do
76
+ expect(subject.status).to eq 200
77
+ expect(subject.headers['Content-Type']).to include 'text/html'
78
+ expect(subject.body).to include 'Network graph'
79
+ end
80
+ end
81
+
82
+ describe "a request for a path that does not exist" do
83
+ let(:path) { '/foo' }
84
+
85
+ it "returns a 404" do
86
+ expect(subject.status).to eq 404
87
+ expect(subject.headers['Content-Type']).to include 'text/html'
88
+ end
89
+ end
90
+
91
+ context "when the HALBrowser is enabled" do
92
+ context "when application/hal+json is also specified as an Accept" do
93
+
94
+ let(:env) { {'HTTP_ACCEPT' => 'text/html;application/hal+json'} }
95
+ let(:path) { '/something' }
96
+
97
+ it "redirects to the HAL Browser" do
98
+ expect(subject.status).to eq 303
99
+ expect(subject.headers['Location']).to eq '/hal-browser/browser.html#/something'
100
+ end
101
+ end
102
+ end
103
+
104
+ context "when the HALBrowser is not enabled" do
105
+ context "when application/hal+json is also specified as an Accept" do
106
+
107
+ let(:hal_browser_enabled) { false }
108
+ let(:env) { {'HTTP_ACCEPT' => 'text/html;application/hal+json'} }
109
+ let(:path) { '/something' }
110
+
111
+ it "does not redirect to the HAL Browser" do
112
+ expect(subject.status).to_not eq 303
113
+ end
114
+ end
115
+ end
116
+
117
+ context "when a .csv extension is specified" do
118
+
119
+ let(:path) { '/groups/Some%20Consumer.csv' }
120
+
121
+ it "returns the CSV Content-Type" do
122
+ expect(subject.status).to eq 200
123
+ expect(subject.headers['Content-Type']).to eq "text/csv"
124
+ end
125
+ end
126
+
127
+ end
128
+
129
+ context "when no Accept header is specified" do
130
+
131
+ let(:env) { {} }
132
+ subject { get path, '', env; last_response }
133
+
134
+ describe "a request for root" do
135
+ let(:path) { "/" }
136
+
137
+ it "returns an API response" do
138
+ expect(subject.status).to eq 200
139
+ expect(subject.headers['Content-Type']).to_not include 'html'
140
+ end
141
+ end
142
+
143
+ describe "a request the group containing a pacticipant" do
144
+ let(:path) { group_url }
145
+
146
+ it "returns the API representation of the group" do
147
+ expect(subject.status).to eq 200
148
+ expect(subject.headers['Content-Type']).to_not include 'html'
149
+ end
150
+ end
151
+
152
+ describe "a request for a stylesheet" do
153
+ let(:path) { '/stylesheets/pact.css' }
154
+
155
+ it "returns the stylesheet" do
156
+ expect(subject.status).to eq 200
157
+ expect(subject.headers['Content-Type']).to include 'text/css'
158
+ end
159
+ end
160
+
161
+ end
162
+
163
+ end
164
+
165
+ end