pact_broker 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +13 -5
- data/.gitignore +2 -0
- data/CHANGELOG.md +5 -0
- data/README.md +2 -1
- data/Rakefile +1 -3
- data/db/migrations/07_increase_json_content_length.rb +6 -1
- data/db/migrations/08_create_latest_pact_view.rb +7 -5
- data/db/migrations/14_add_timestamps_to_pact_views.rb +28 -0
- data/lib/pact_broker/api.rb +0 -4
- data/lib/pact_broker/api/decorators/latest_pact_decorator.rb +4 -1
- data/lib/pact_broker/api/decorators/pact_decorator.rb +2 -2
- data/lib/pact_broker/api/decorators/pact_version_decorator.rb +3 -4
- data/lib/pact_broker/api/decorators/pact_versions_decorator.rb +4 -4
- data/lib/pact_broker/api/decorators/pacticipant_decorator.rb +3 -2
- data/lib/pact_broker/api/decorators/tag_decorator.rb +2 -2
- data/lib/pact_broker/api/decorators/timestamps.rb +24 -0
- data/lib/pact_broker/api/decorators/webhook_decorator.rb +2 -2
- data/lib/pact_broker/api/decorators/webhooks_decorator.rb +4 -4
- data/lib/pact_broker/api/renderers/html_pact_renderer.rb +16 -4
- data/lib/pact_broker/api/resources/base_resource.rb +0 -1
- data/lib/pact_broker/api/resources/group.rb +11 -6
- data/lib/pact_broker/api/resources/index.rb +5 -6
- data/lib/pact_broker/api/resources/latest_pact.rb +8 -5
- data/lib/pact_broker/api/resources/latest_pacts.rb +3 -3
- data/lib/pact_broker/api/resources/pact.rb +9 -5
- data/lib/pact_broker/api/resources/pact_webhooks.rb +9 -5
- data/lib/pact_broker/api/resources/pacticipant.rb +10 -5
- data/lib/pact_broker/api/resources/relationships.rb +3 -3
- data/lib/pact_broker/api/resources/tag.rb +8 -7
- data/lib/pact_broker/api/resources/webhook.rb +1 -1
- data/lib/pact_broker/api/resources/webhook_execution.rb +1 -1
- data/lib/pact_broker/app.rb +8 -57
- data/lib/pact_broker/logging.rb +1 -0
- data/lib/pact_broker/models/pact.rb +6 -8
- data/lib/pact_broker/repositories/pact.rb +29 -0
- data/lib/pact_broker/repositories/pact_content.rb +0 -0
- data/lib/pact_broker/repositories/pact_repository.rb +66 -21
- data/lib/pact_broker/repositories/tag_repository.rb +1 -1
- data/lib/pact_broker/repositories/version_repository.rb +2 -1
- data/lib/pact_broker/services/pact_service.rb +14 -16
- data/lib/pact_broker/ui.rb +2 -0
- data/lib/pact_broker/ui/app.rb +81 -0
- data/lib/pact_broker/ui/controllers/groups.rb +1 -1
- data/lib/pact_broker/version.rb +1 -1
- data/lib/rack/pact_broker/convert_file_extension_to_accept_header.rb +44 -0
- data/pact_broker.gemspec +1 -0
- data/public/javascripts/highlight.pack.js +1 -0
- data/public/stylesheets/github-json.css +127 -0
- data/public/stylesheets/pact.css +4 -0
- data/spec/fixtures/consumer-provider.json +21 -0
- data/spec/integration/app_spec.rb +165 -0
- data/spec/integration/endpoints/pact_put_spec.rb +43 -0
- data/spec/lib/pact_broker/api/decorators/latest_pact_decorator_spec.rb +42 -0
- data/spec/lib/pact_broker/api/decorators/pact_version_decorator_spec.rb +5 -0
- data/spec/lib/pact_broker/api/decorators/pacticipant_decorator_spec.rb +2 -1
- data/spec/lib/pact_broker/api/decorators/webhook_decorator_spec.rb +9 -2
- data/spec/lib/pact_broker/api/renderers/html_pact_renderer_spec.rb +14 -3
- data/spec/lib/pact_broker/repositories/pact_repository_spec.rb +88 -4
- data/spec/lib/pact_broker/repositories/tag_repository_spec.rb +2 -0
- data/spec/lib/pact_broker/repositories/version_repository_spec.rb +20 -2
- data/spec/lib/pact_broker/services/pacticipant_service_spec.rb +1 -1
- data/spec/service_consumers/pact_helper.rb +2 -17
- data/spec/spec_helper.rb +4 -22
- data/spec/support/database_cleaner.rb +18 -0
- data/spec/support/fixture_helpers.rb +10 -0
- data/spec/support/provider_state_builder.rb +4 -15
- 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
|
-
|
29
|
+
existing_pact = pact_repository.find_by_version_and_provider(consumer_version.id, provider.id)
|
30
30
|
|
31
|
-
if
|
32
|
-
|
31
|
+
if existing_pact
|
32
|
+
update_pact params, existing_pact
|
33
33
|
else
|
34
|
-
|
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,
|
67
|
-
|
68
|
-
|
69
|
-
if
|
70
|
-
webhook_service.execute_webhooks
|
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
|
-
|
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
|
-
|
78
|
-
|
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,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
|
data/lib/pact_broker/version.rb
CHANGED
@@ -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
@@ -0,0 +1 @@
|
|
1
|
+
var hljs=new function(){function j(v){return v.replace(/&/gm,"&").replace(/</gm,"<").replace(/>/gm,">")}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
|
+
}
|
data/public/stylesheets/pact.css
CHANGED
@@ -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
|