<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]--> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="generator" content="Asciidoctor 1.5.2"> <meta name="author" content="Tristan Juricek"> <title>Perforce Web Services Guide: Internal Alpha</title> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400"> <style> /* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */ /* Remove the comments around the @import statement below when using this as a custom stylesheet */ /*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400";*/ article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block} audio,canvas,video{display:inline-block} audio:not([controls]){display:none;height:0} [hidden],template{display:none} script{display:none!important} html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%} body{margin:0} a{background:transparent} a:focus{outline:thin dotted} a:active,a:hover{outline:0} h1{font-size:2em;margin:.67em 0} abbr[title]{border-bottom:1px dotted} b,strong{font-weight:bold} dfn{font-style:italic} hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0} mark{background:#ff0;color:#000} code,kbd,pre,samp{font-family:monospace;font-size:1em} pre{white-space:pre-wrap} q{quotes:"\201C" "\201D" "\2018" "\2019"} small{font-size:80%} sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline} sup{top:-.5em} sub{bottom:-.25em} img{border:0} svg:not(:root){overflow:hidden} figure{margin:0} fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em} legend{border:0;padding:0} button,input,select,textarea{font-family:inherit;font-size:100%;margin:0} button,input{line-height:normal} button,select{text-transform:none} button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer} button[disabled],html input[disabled]{cursor:default} input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0} input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box} input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none} button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0} textarea{overflow:auto;vertical-align:top} table{border-collapse:collapse;border-spacing:0} *,*:before,*:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box} html,body{font-size:100%} body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto} a:hover{cursor:pointer} img,object,embed{max-width:100%;height:auto} object,embed{height:100%} img{-ms-interpolation-mode:bicubic} #map_canvas img,#map_canvas embed,#map_canvas object,.map_canvas img,.map_canvas embed,.map_canvas object{max-width:none!important} .left{float:left!important} .right{float:right!important} .text-left{text-align:left!important} .text-right{text-align:right!important} .text-center{text-align:center!important} .text-justify{text-align:justify!important} .hide{display:none} .antialiased,body{-webkit-font-smoothing:antialiased} img{display:inline-block;vertical-align:middle} textarea{height:auto;min-height:50px} select{width:100%} p.lead,.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{font-size:1.21875em;line-height:1.6} .subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em} div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr} a{color:#2156a5;text-decoration:underline;line-height:inherit} a:hover,a:focus{color:#1d4b8f} a img{border:none} p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility} p aside{font-size:.875em;line-height:1.35;font-style:italic} h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em} h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0} h1{font-size:2.125em} h2{font-size:1.6875em} h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em} h4,h5{font-size:1.125em} h6{font-size:1em} hr{border:solid #ddddd8;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0} em,i{font-style:italic;line-height:inherit} strong,b{font-weight:bold;line-height:inherit} small{font-size:60%;line-height:inherit} code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)} ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit} ul,ol,ul.no-bullet,ol.no-bullet{margin-left:1.5em} ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em} ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit} ul.square{list-style-type:square} ul.circle{list-style-type:circle} ul.disc{list-style-type:disc} ul.no-bullet{list-style:none} ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0} dl dt{margin-bottom:.3125em;font-weight:bold} dl dd{margin-bottom:1.25em} abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help} abbr{text-transform:none} blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd} blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)} blockquote cite:before{content:"\2014 \0020"} blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)} blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)} @media only screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2} h1{font-size:2.75em} h2{font-size:2.3125em} h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em} h4{font-size:1.4375em}}table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede} table thead,table tfoot{background:#f7f8f7;font-weight:bold} table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left} table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)} table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7} table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6} h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em} h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400} .clearfix:before,.clearfix:after,.float-group:before,.float-group:after{content:" ";display:table} .clearfix:after,.float-group:after{clear:both} *:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed} pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed} .keyseq{color:rgba(51,51,51,.8)} kbd{display:inline-block;color:rgba(0,0,0,.8);font-size:.75em;line-height:1.4;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:-.15em .15em 0 .15em;padding:.2em .6em .2em .5em;vertical-align:middle;white-space:nowrap} .keyseq kbd:first-child{margin-left:0} .keyseq kbd:last-child{margin-right:0} .menuseq,.menu{color:rgba(0,0,0,.8)} b.button:before,b.button:after{position:relative;top:-1px;font-weight:400} b.button:before{content:"[";padding:0 3px 0 2px} b.button:after{content:"]";padding:0 2px 0 3px} p a>code:hover{color:rgba(0,0,0,.9)} #header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em} #header:before,#header:after,#content:before,#content:after,#footnotes:before,#footnotes:after,#footer:before,#footer:after{content:" ";display:table} #header:after,#content:after,#footnotes:after,#footer:after{clear:both} #content{margin-top:1.25em} #content:before{content:none} #header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0} #header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #ddddd8} #header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #ddddd8;padding-bottom:8px} #header .details{border-bottom:1px solid #ddddd8;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap} #header .details span:first-child{margin-left:-.125em} #header .details span.email a{color:rgba(0,0,0,.85)} #header .details br{display:none} #header .details br+span:before{content:"\00a0\2013\00a0"} #header .details br+span.author:before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)} #header .details br+span#revremark:before{content:"\00a0|\00a0"} #header #revnumber{text-transform:capitalize} #header #revnumber:after{content:"\00a0"} #content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #ddddd8;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem} #toc{border-bottom:1px solid #efefed;padding-bottom:.5em} #toc>ul{margin-left:.125em} #toc ul.sectlevel0>li>a{font-style:italic} #toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0} #toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none} #toc a{text-decoration:none} #toc a:active{text-decoration:underline} #toctitle{color:#7a2518;font-size:1.2em} @media only screen and (min-width:768px){#toctitle{font-size:1.375em} body.toc2{padding-left:15em;padding-right:0} #toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #efefed;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto} #toc.toc2 #toctitle{margin-top:0;font-size:1.2em} #toc.toc2>ul{font-size:.9em;margin-bottom:0} #toc.toc2 ul ul{margin-left:0;padding-left:1em} #toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em} body.toc2.toc-right{padding-left:0;padding-right:15em} body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #efefed;left:auto;right:0}}@media only screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0} #toc.toc2{width:20em} #toc.toc2 #toctitle{font-size:1.375em} #toc.toc2>ul{font-size:.95em} #toc.toc2 ul ul{padding-left:1.25em} body.toc2.toc-right{padding-left:0;padding-right:20em}}#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px} #content #toc>:first-child{margin-top:0} #content #toc>:last-child{margin-bottom:0} #footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em} #footer-text{color:rgba(255,255,255,.8);line-height:1.44} .sect1{padding-bottom:.625em} @media only screen and (min-width:768px){.sect1{padding-bottom:1.25em}}.sect1+.sect1{border-top:1px solid #efefed} #content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400} #content h1>a.anchor:before,h2>a.anchor:before,h3>a.anchor:before,#toctitle>a.anchor:before,.sidebarblock>.content>.title>a.anchor:before,h4>a.anchor:before,h5>a.anchor:before,h6>a.anchor:before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em} #content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible} #content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none} #content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221} .audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em} .admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic} table.tableblock>caption.title{white-space:nowrap;overflow:visible;max-width:0} .paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{color:rgba(0,0,0,.85)} table.tableblock #preamble>.sectionbody>.paragraph:first-of-type p{font-size:inherit} .admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%} .admonitionblock>table td.icon{text-align:center;width:80px} .admonitionblock>table td.icon img{max-width:none} .admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase} .admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #ddddd8;color:rgba(0,0,0,.6)} .admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0} .exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px} .exampleblock>.content>:first-child{margin-top:0} .exampleblock>.content>:last-child{margin-bottom:0} .sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px} .sidebarblock>:first-child{margin-top:0} .sidebarblock>:last-child{margin-bottom:0} .sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center} .exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0} .literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8} .sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1} .literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;padding:1em;font-size:.8125em} .literalblock pre.nowrap,.literalblock pre[class].nowrap,.listingblock pre.nowrap,.listingblock pre[class].nowrap{overflow-x:auto;white-space:pre;word-wrap:normal} @media only screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}@media only screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)} .listingblock pre.highlightjs{padding:0} .listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px} .listingblock pre.prettyprint{border-width:0} .listingblock>.content{position:relative} .listingblock code[data-lang]:before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999} .listingblock:hover code[data-lang]:before{display:block} .listingblock.terminal pre .command:before{content:attr(data-prompt);padding-right:.5em;color:#999} .listingblock.terminal pre .command:not([data-prompt]):before{content:"$"} table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:none} table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0} table.pyhltable td.code{padding-left:.75em;padding-right:0} pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #ddddd8} pre.pygments .lineno{display:inline-block;margin-right:.25em} table.pyhltable .linenodiv{background:none!important;padding-right:0!important} .quoteblock{margin:0 1em 1.25em 1.5em;display:table} .quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em} .quoteblock blockquote,.quoteblock blockquote p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify} .quoteblock blockquote{margin:0;padding:0;border:0} .quoteblock blockquote:before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)} .quoteblock blockquote>.paragraph:last-child p{margin-bottom:0} .quoteblock .attribution{margin-top:.5em;margin-right:.5ex;text-align:right} .quoteblock .quoteblock{margin-left:0;margin-right:0;padding:.5em 0;border-left:3px solid rgba(0,0,0,.6)} .quoteblock .quoteblock blockquote{padding:0 0 0 .75em} .quoteblock .quoteblock blockquote:before{display:none} .verseblock{margin:0 1em 1.25em 1em} .verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility} .verseblock pre strong{font-weight:400} .verseblock .attribution{margin-top:1.25rem;margin-left:.5ex} .quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic} .quoteblock .attribution br,.verseblock .attribution br{display:none} .quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.05em;color:rgba(0,0,0,.6)} .quoteblock.abstract{margin:0 0 1.25em 0;display:block} .quoteblock.abstract blockquote,.quoteblock.abstract blockquote p{text-align:left;word-spacing:0} .quoteblock.abstract blockquote:before,.quoteblock.abstract blockquote p:first-of-type:before{display:none} table.tableblock{max-width:100%;border-collapse:separate} table.tableblock td>.paragraph:last-child p>p:last-child,table.tableblock th>p:last-child,table.tableblock td>p:last-child{margin-bottom:0} table.spread{width:100%} table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede} table.grid-all th.tableblock,table.grid-all td.tableblock{border-width:0 1px 1px 0} table.grid-all tfoot>tr>th.tableblock,table.grid-all tfoot>tr>td.tableblock{border-width:1px 1px 0 0} table.grid-cols th.tableblock,table.grid-cols td.tableblock{border-width:0 1px 0 0} table.grid-all *>tr>.tableblock:last-child,table.grid-cols *>tr>.tableblock:last-child{border-right-width:0} table.grid-rows th.tableblock,table.grid-rows td.tableblock{border-width:0 0 1px 0} table.grid-all tbody>tr:last-child>th.tableblock,table.grid-all tbody>tr:last-child>td.tableblock,table.grid-all thead:last-child>tr>th.tableblock,table.grid-rows tbody>tr:last-child>th.tableblock,table.grid-rows tbody>tr:last-child>td.tableblock,table.grid-rows thead:last-child>tr>th.tableblock{border-bottom-width:0} table.grid-rows tfoot>tr>th.tableblock,table.grid-rows tfoot>tr>td.tableblock{border-width:1px 0 0 0} table.frame-all{border-width:1px} table.frame-sides{border-width:0 1px} table.frame-topbot{border-width:1px 0} th.halign-left,td.halign-left{text-align:left} th.halign-right,td.halign-right{text-align:right} th.halign-center,td.halign-center{text-align:center} th.valign-top,td.valign-top{vertical-align:top} th.valign-bottom,td.valign-bottom{vertical-align:bottom} th.valign-middle,td.valign-middle{vertical-align:middle} table thead th,table tfoot th{font-weight:bold} tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7} tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold} p.tableblock>code:only-child{background:none;padding:0} p.tableblock{font-size:1em} td>div.verse{white-space:pre} ol{margin-left:1.75em} ul li ol{margin-left:1.5em} dl dd{margin-left:1.125em} dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0} ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em} ul.unstyled,ol.unnumbered,ul.checklist,ul.none{list-style-type:none} ul.unstyled,ol.unnumbered,ul.checklist{margin-left:.625em} ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1em;font-size:.85em} ul.checklist li>p:first-child>input[type="checkbox"]:first-child{width:1em;position:relative;top:1px} ul.inline{margin:0 auto .625em auto;margin-left:-1.375em;margin-right:0;padding:0;list-style:none;overflow:hidden} ul.inline>li{list-style:none;float:left;margin-left:1.375em;display:block} ul.inline>li>*{display:block} .unstyled dl dt{font-weight:400;font-style:normal} ol.arabic{list-style-type:decimal} ol.decimal{list-style-type:decimal-leading-zero} ol.loweralpha{list-style-type:lower-alpha} ol.upperalpha{list-style-type:upper-alpha} ol.lowerroman{list-style-type:lower-roman} ol.upperroman{list-style-type:upper-roman} ol.lowergreek{list-style-type:lower-greek} .hdlist>table,.colist>table{border:0;background:none} .hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none} td.hdlist1{padding-right:.75em;font-weight:bold} td.hdlist1,td.hdlist2{vertical-align:top} .literalblock+.colist,.listingblock+.colist{margin-top:-.5em} .colist>table tr>td:first-of-type{padding:0 .75em;line-height:1} .colist>table tr>td:last-of-type{padding:.25em 0} .thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd} .imageblock.left,.imageblock[style*="float: left"]{margin:.25em .625em 1.25em 0} .imageblock.right,.imageblock[style*="float: right"]{margin:.25em 0 1.25em .625em} .imageblock>.title{margin-bottom:0} .imageblock.thumb,.imageblock.th{border-width:6px} .imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em} .image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0} .image.left{margin-right:.625em} .image.right{margin-left:.625em} a.image{text-decoration:none} span.footnote,span.footnoteref{vertical-align:super;font-size:.875em} span.footnote a,span.footnoteref a{text-decoration:none} span.footnote a:active,span.footnoteref a:active{text-decoration:underline} #footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em} #footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em 0;border-width:1px 0 0 0} #footnotes .footnote{padding:0 .375em;line-height:1.3;font-size:.875em;margin-left:1.2em;text-indent:-1.2em;margin-bottom:.2em} #footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none} #footnotes .footnote:last-of-type{margin-bottom:0} #content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0} .gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0} .gist .file-data>table td.line-data{width:99%} div.unbreakable{page-break-inside:avoid} .big{font-size:larger} .small{font-size:smaller} .underline{text-decoration:underline} .overline{text-decoration:overline} .line-through{text-decoration:line-through} .aqua{color:#00bfbf} .aqua-background{background-color:#00fafa} .black{color:#000} .black-background{background-color:#000} .blue{color:#0000bf} .blue-background{background-color:#0000fa} .fuchsia{color:#bf00bf} .fuchsia-background{background-color:#fa00fa} .gray{color:#606060} .gray-background{background-color:#7d7d7d} .green{color:#006000} .green-background{background-color:#007d00} .lime{color:#00bf00} .lime-background{background-color:#00fa00} .maroon{color:#600000} .maroon-background{background-color:#7d0000} .navy{color:#000060} .navy-background{background-color:#00007d} .olive{color:#606000} .olive-background{background-color:#7d7d00} .purple{color:#600060} .purple-background{background-color:#7d007d} .red{color:#bf0000} .red-background{background-color:#fa0000} .silver{color:#909090} .silver-background{background-color:#bcbcbc} .teal{color:#006060} .teal-background{background-color:#007d7d} .white{color:#bfbfbf} .white-background{background-color:#fafafa} .yellow{color:#bfbf00} .yellow-background{background-color:#fafa00} span.icon>.fa{cursor:default} .admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default} .admonitionblock td.icon .icon-note:before{content:"\f05a";color:#19407c} .admonitionblock td.icon .icon-tip:before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111} .admonitionblock td.icon .icon-warning:before{content:"\f071";color:#bf6900} .admonitionblock td.icon .icon-caution:before{content:"\f06d";color:#bf3400} .admonitionblock td.icon .icon-important:before{content:"\f06a";color:#bf0000} .conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold} .conum[data-value] *{color:#fff!important} .conum[data-value]+b{display:none} .conum[data-value]:after{content:attr(data-value)} pre .conum[data-value]{position:relative;top:-.125em} b.conum *{color:inherit!important} .conum:not([data-value]):empty{display:none} h1,h2{letter-spacing:-.01em} dt,th.tableblock,td.content{text-rendering:optimizeLegibility} p,td.content{letter-spacing:-.01em} p strong,td.content strong{letter-spacing:-.005em} p,blockquote,dt,td.content{font-size:1.0625rem} p{margin-bottom:1.25rem} .sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em} .exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc} .print-only{display:none!important} @media print{@page{margin:1.25cm .75cm} *{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important} a{color:inherit!important;text-decoration:underline!important} a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important} a[href^="http:"]:not(.bare):after,a[href^="https:"]:not(.bare):after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em} abbr[title]:after{content:" (" attr(title) ")"} pre,blockquote,tr,img{page-break-inside:avoid} thead{display:table-header-group} img{max-width:100%!important} p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3} h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid} #toc,.sidebarblock,.exampleblock>.content{background:none!important} #toc{border-bottom:1px solid #ddddd8!important;padding-bottom:0!important} .sect1{padding-bottom:0!important} .sect1+.sect1{border:0!important} #header>h1:first-child{margin-top:1.25rem} body.book #header{text-align:center} body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em 0} body.book #header .details{border:0!important;display:block;padding:0!important} body.book #header .details span:first-child{margin-left:0!important} body.book #header .details br{display:block} body.book #header .details br+span:before{content:none!important} body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important} body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always} .listingblock code[data-lang]:before{display:block} #footer{background:none!important;padding:0 .9375em} #footer-text{color:rgba(0,0,0,.6)!important;font-size:.9em} .hide-on-print{display:none!important} .print-only{display:block!important} .hide-for-print{display:none!important} .show-for-print{display:inherit!important}} </style> </head> <body class="article toc2 toc-right"> <div id="header"> <h1>Perforce Web Services Guide: Internal Alpha</h1> <div class="details"> <span id="author" class="author">Tristan Juricek</span><br> <span id="email" class="email"><a href="mailto:tjuricek@perforce.com">tjuricek@perforce.com</a></span><br> <span id="revnumber">version 2015.1.alpha,</span> <span id="revdate">May 2015</span> </div> <div id="toc" class="toc2"> <div id="toctitle">Table of Contents</div> <ul class="sectlevel1"> <li><a href="#_about_this_manual">About This Manual</a> <ul class="sectlevel2"> <li><a href="#_please_give_us_feedback">Please give us feedback</a></li> </ul> </li> <li><a href="#_overview">Overview</a> <ul class="sectlevel2"> <li><a href="#_knowledge_required">Knowledge Required</a></li> <li><a href="#_release_compatibility_of_the_api">Release compatibility of the API</a></li> <li><a href="#_architecture_of_the_api">Architecture of the API</a></li> </ul> </li> <li><a href="#_deploying_helix_web_services">Deploying Helix Web Services</a> <ul class="sectlevel2"> <li><a href="#_deploying_from_a_source_tarball">Deploying from a source tarball</a></li> <li><a href="#_deploying_from_packages">Deploying from Packages</a></li> </ul> </li> <li><a href="#system_configuration">Configuration</a></li> <li><a href="#_client_application_development">Client Application Development</a> <ul class="sectlevel2"> <li><a href="#authentication">Authentication</a></li> <li><a href="#per_request_configuration">Per-Request Configuration</a></li> <li><a href="#error_responses">Error Conventions</a></li> <li><a href="#ruby_client_sdk_overview">Ruby Client SDK</a></li> </ul> </li> <li><a href="#_http_protocol_reference">HTTP Protocol Reference</a> <ul class="sectlevel2"> <li><a href="#_authentication_methods">Authentication Methods</a></li> <li><a href="#_branches">Branches</a></li> </ul> </li> <li><a href="#_client_sdk_reference">Client SDK Reference</a></li> <li><a href="#_server_programming">Server Programming</a> <ul class="sectlevel2"> <li><a href="#_including_new_rack_middleware">Including New Rack Middleware</a></li> <li><a href="#_registering_configuration_variables">Registering Configuration Variables</a></li> </ul> </li> <li><a href="#_server_api_reference">Server API Reference</a></li> </ul> </div> </div> <div id="content"> <div class="sect1"> <h2 id="_about_this_manual"><a class="link" href="#_about_this_manual">About This Manual</a></h2> <div class="sectionbody"> <div class="paragraph"> <p>This guide covers administering and developing against the Helix Web Services. Helix Web Services provide common services and APIs that interact with the Helix Versioning Engine.</p> </div> <div class="paragraph"> <p>A Helix Web Services server is a web server, and it’s APIs are fundamentally based on the HTTP protocol. Not all aspects of the Helix Versioning Engine are provided by Helix Web Services, vice versa. Some services provided by Helix Web Services are not available in the Helix Versioning Engine.</p> </div> <div class="sect2"> <h3 id="_please_give_us_feedback"><a class="link" href="#_please_give_us_feedback">Please give us feedback</a></h3> <div class="paragraph"> <p>If you have any feedback for us, or detect any errors in this guide, please email details to <a href="mailto:manual@perforce.com">manual@perforce.com</a>.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="_overview"><a class="link" href="#_overview">Overview</a></h2> <div class="sectionbody"> <div class="sect2"> <h3 id="_knowledge_required"><a class="link" href="#_knowledge_required">Knowledge Required</a></h3> <div class="paragraph"> <p>For deployment and configuration tasks, this guide assumes that you are experienced with administering web applications. You should be conceptually comfortable with Nginx and Unicorn. It is not necessary to have expert knowledge of these systems, but you should understand what their roles are without further clarification. If you require more knowledge, please consult online guides available from each of the primary sites:</p> </div> <div class="ulist"> <ul> <li> <p>Nginx: <a href="http://nginx.org/" class="bare">http://nginx.org/</a></p> </li> <li> <p>Unicorn: <a href="http://unicorn.bogomips.org/" class="bare">http://unicorn.bogomips.org/</a></p> </li> </ul> </div> <div class="paragraph"> <p>Additionally, while this guide interfaces with the Helix Versioning Engine, we assume you are familiar with it. For more information about the Helix Versioning Engine, please become familiar with the following online guides:</p> </div> <div class="ulist"> <ul> <li> <p>Product overview: <a href="http://www.perforce.com/versioning-engine" class="bare">http://www.perforce.com/versioning-engine</a></p> </li> <li> <p>P4 Command Reference: <a href="http://www.perforce.com/perforce/doc.current/manuals/cmdref/index.html" class="bare">http://www.perforce.com/perforce/doc.current/manuals/cmdref/index.html</a></p> </li> <li> <p>Admin fundamentals: <a href="http://www.perforce.com/perforce/doc.current/manuals/p4sag/index.html" class="bare">http://www.perforce.com/perforce/doc.current/manuals/p4sag/index.html</a></p> </li> </ul> </div> <div class="paragraph"> <p>For client development tasks, this guide assumes you are comfortable with the HTTP protocol. While it is not required to know Ruby, we provide a client SDK for Ruby, which may be easier to understand initially. If you do not use our Ruby client SDK, you will need to create an SDK for your platform, likely combined with a JSON parser.</p> </div> </div> <div class="sect2"> <h3 id="_release_compatibility_of_the_api"><a class="link" href="#_release_compatibility_of_the_api">Release compatibility of the API</a></h3> <div class="paragraph"> <p>Helix Web Services are intended to be executed against a corresponding major release of the Helix Versioning Engine. For example, the '2015.1' release of Helix Web Services, is intended to interact with the '2015.1' of the Helix Versioning Engine. Using Helix Web Services against a non-corresponding Helix Versioning Engine instance will likely work, with some caveats.</p> </div> <div class="paragraph"> <p>The client SDK provided for Helix Web Services contains several 'model' objects that help assist in normalizing data from the Helix Versioning Engine. When you use a different version of Helix Web Services than your Helix Versioning Engine, it is likely that some data may not exist. You may not want to use the model instances when using mismatching versions, and focus more on the more generic hash-based methods.</p> </div> </div> <div class="sect2"> <h3 id="_architecture_of_the_api"><a class="link" href="#_architecture_of_the_api">Architecture of the API</a></h3> <div class="paragraph"> <p>Helix Web Services is a Ruby web application, that is intended to sit behind a reverse proxy server, such as nginx. The reverse proxy server should handle the configuration of SSL for the web services, and can host other web applications from the Helix suite. Helix Web Services currently deploys into a single unicorn instance, on Linux machines.</p> </div> <div class="paragraph"> <p>Each Helix Web Services instance should not maintain significant state. You should be able to launch more than one Helix Web Services instance, and configure your reverse proxy to handle load balancing. You can also architect your system using services such as haproxy for load balancing. Using haproxy and nginx is beyond the scope of this documentation guide; we assume you are an experienced systems administrator when it comes to clustered web application deployment.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="_deploying_helix_web_services"><a class="link" href="#_deploying_helix_web_services">Deploying Helix Web Services</a></h2> <div class="sectionbody"> <div class="paragraph"> <p>Helix Web Services can be deployed from a source tarball distribution or from installed packages. Starting up web services from a source tarball is similar to working with code checked out from our source distribution.</p> </div> <div class="paragraph"> <p>To be clear, Helix Web Services will be set up to run a single application server instance. This application server will run on an unprivileged port, like 9000. It’s expected that Helix Web Services would be a part of a greater ecosystem of web applications, likely proxied by a web server such as Nginx. Our package installation includes a post-install configuration script that configures other services on the local machine to make it easy.</p> </div> <div class="sect2"> <h3 id="_deploying_from_a_source_tarball"><a class="link" href="#_deploying_from_a_source_tarball">Deploying from a source tarball</a></h3> <div class="paragraph"> <p>The following steps will launch a Helix Web Services instance in the current terminal session. This is not intended for a production installation, but may be valid for evaluating the product or experimenting with customizing Helix Web Services from source.</p> </div> <div class="paragraph"> <p>Prerequisites to working with our source tarball:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Ruby 2.2</p> </li> <li> <p>An installed Perforce server</p> </li> </ol> </div> <div class="paragraph"> <p>Other Ruby variations will likely work, we do not test them.</p> </div> <div class="paragraph"> <p>Installation steps:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Run: <code>tar xzf helix-web-services-[VERSION].tar.gz</code></p> </li> <li> <p>Run: <code>cd helix-web-services-[VERSION]/helix_web_services</code></p> </li> <li> <p>Run: <code>bundle install</code></p> </li> <li> <p>Run: <code>bundle exec foreman start</code></p> </li> </ol> </div> </div> <div class="sect2"> <h3 id="_deploying_from_packages"><a class="link" href="#_deploying_from_packages">Deploying from Packages</a></h3> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Right now you can only obtain packages from an internal build instance of Perforce. This assumes you have obtained the appropriate tarball, and unpacked it. </td> </tr> </table> </div> <div class="paragraph"> <p>Install the package using the appropriate platform tool:</p> </div> <div class="ulist"> <ul> <li> <p>On Debian or Ubuntu systems, run <code>dpkg -i helix-web-services-[VERSION].deb</code></p> </li> <li> <p>On Redhat or CentOS systems, run <code>rpm -i helix-web-services-[VERSION].rpm</code></p> </li> </ul> </div> <div class="paragraph"> <p>After installation, run the configuration script:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code>sudo /opt/perforce/helix-web-services/bin/hws_configure</code></pre> </div> </div> <div class="paragraph"> <p>If this machine already has an existing nginx or perforce server configuration, you will have to manually configure /etc/nginx/nginx.conf or adjust the system configuration for Helix Web Services. See <a href="#system_configuration">Configuration</a>.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="system_configuration"><a class="link" href="#system_configuration">Configuration</a></h2> <div class="sectionbody"> <div class="paragraph"> <p>System default settings for Helix Web Services can be set by creating a YAML file at <code>/etc/perforce/helix_web_services.conf</code>. If you’re unfamiliar with YAML, you simply use the variables below in simple key-value pairs. Pay attention, however, to the type of the variable. In many cases we use strings when you might have thought it were a number.</p> </div> <div class="paragraph"> <p>For example:</p> </div> <div class="paragraph"> <p>/etc/perforce/helix_web_services.conf</p> </div> <div class="listingblock"> <div class="content"> <pre>P4HOST: 'perforce.mycompany.com' P4PORT: '1999'</pre> </div> </div> <div class="paragraph"> <p>Many settings in Helix Web Services are overridable by HTTP headers on a per-request basis. The "Overridable" column in the table below means you can specify this header. If it is not available, you can <em>only</em> specify the configuration in the system config file. For more information on specifying the system header, see <a href="#per_request_configuration">Per-Request Configuration</a>.</p> </div> <table class="tableblock frame-all grid-all spread"> <colgroup> <col style="width: 20%;"> <col style="width: 20%;"> <col style="width: 20%;"> <col style="width: 20%;"> <col style="width: 20%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Variable</th> <th class="tableblock halign-left valign-top">Type</th> <th class="tableblock halign-left valign-top">Overridable</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-left valign-top">Default</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">P4APILEVEL</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">String</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Yes</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The API level to use for interacting with the particular Perforce server. Our client defaults this to the</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">78 (Corresponds to 2015.1)</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">P4HOST</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">String</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Yes</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The hostname for the perforce server, e.g., <code>perforce.example.com</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>localhost</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">P4PORT</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">String</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Yes</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The port number (or port and hostname combination) that indicates our Perforce connection</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>1666</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">P4URL</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">String</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Yes</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A <code>p4:</code> URL that indicates hostname and port, e.g., <code>p4://perforce.example.com:1666</code></p></td> <td class="tableblock halign-left valign-top"></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">P4CHARSET</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">String</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Yes</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The charset value for P4 connections. Set to <code>none</code> if you are connecting to a non-unicode server.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>auto</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">WORKSPACE_PATH</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">String</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The local directory used for generating temporary Perforce Client Workspaces to do some basic operations.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>/var/lib/perforce/helix_web_services/workspaces</code></p></td> </tr> </tbody> </table> </div> </div> <div class="sect1"> <h2 id="_client_application_development"><a class="link" href="#_client_application_development">Client Application Development</a></h2> <div class="sectionbody"> <div class="paragraph"> <p>In general, your client application is going to need to:</p> </div> <div class="ulist"> <ul> <li> <p>Know the Helix Versioning Engine topology</p> </li> <li> <p>Obtain login tokens for Helix Versioning Engine (using <a href="#post_auth_v1_login"><code>POST /auth/v1/login</code></a>)</p> </li> <li> <p>Call other methods on the system.</p> </li> </ul> </div> <div class="paragraph"> <p>Your application may use a single Helix Web Services instance to interact with multiple Helix Versioning Engines. In these cases, you will need to specify which versioning engine you are interacting with. In cases you are only using one Helix Versioning Engine, your application can then specify system wide settings. See <a href="#system_configuration">Configuration</a> for more details.</p> </div> <div class="paragraph"> <p>Working with Helix Web Services for a client generally involves making a single request at a time. Many of our core methods mimic the set of commands available to the <code>p4</code> application. But if you need to make a combined set of `p4 commands, ideally, there are extended "services" available, that handle that interaction. So, in general, your client application only needs to make one web service call at a time.</p> </div> <div class="sect2"> <h3 id="authentication"><a class="link" href="#authentication">Authentication</a></h3> <div class="paragraph"> <p>Almost every Helix Web Services method requires authentication. We support <a href="http://tools.ietf.org/html/rfc2617">HTTP Basic Authentication</a>, using the login and p4 ticket as credentials. Obtain a ticket via <a href="#post_auth_v1_login"><code>POST /auth/v1/login</code></a>.</p> </div> <div class="paragraph"> <p>Say the user <code>jdoe</code> has obtained the ticket <code>B984EF267963F434BB51C063EE86E8F9</code>. The corresponding HTTP header to add to each request would be:</p> </div> <div class="paragraph"> <p>…​ Authorization: Basic amRvZTpCOTg0RUYyNjc5NjNGNDM0QkI1MUMwNjNFRTg2RThGOQ== …​</p> </div> </div> <div class="sect2"> <h3 id="per_request_configuration"><a class="link" href="#per_request_configuration">Per-Request Configuration</a></h3> <div class="paragraph"> <p>Each request can specify HTTP headers to indicate override known settings the system.</p> </div> <div class="paragraph"> <p>Each custom header takes the format:</p> </div> <div class="literalblock"> <div class="content"> <pre>X-Perforce-Helix_Web_Services-[KEY]</pre> </div> </div> <div class="paragraph"> <p>See <a href="#system_configuration">Configuration</a> for the list of available keys.</p> </div> <div class="paragraph"> <p>For example, to change the <code>P4PORT</code> to be used, which identifies a separate Helix Versioning Engine, you’d set this header:</p> </div> <div class="literalblock"> <div class="content"> <pre>X-Perforce-Helix_Web_Services-P4PORT: helix-eu.mycompany.com:1666</pre> </div> </div> </div> <div class="sect2"> <h3 id="error_responses"><a class="link" href="#error_responses">Error Conventions</a></h3> <div class="paragraph"> <p>Upon success, methods of Helix Web Services return the 200 HTTP status code. If your authentication token is not valid, you will receive 403 error, which means you need to update your login token (using <a href="#post_auth_v1_login"><code>POST /auth/v1/login</code></a>).</p> </div> <div class="paragraph"> <p>401 errors will likely originate from the Helix Versioning Engine. In this case, we provide information to the client application via a JSON object. This object will contain three main properties:</p> </div> <table class="tableblock frame-all grid-all spread"> <colgroup> <col style="width: 50%;"> <col style="width: 50%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Property</th> <th class="tableblock halign-left valign-top">Description</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>MessageCode</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A numeric ID for the problem, typically defined by the Helix Versioning Engine. Should be the same value as <a href="http://www.perforce.com/perforce/r15.1/manuals/p4api/chapter.methods.html#error.getgeneric">Error::GetGeneric()</a>.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>MessageSeverity</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Mirrors the severity levels of <a href="http://www.perforce.com/perforce/r15.1/manuals/p4api/chapter.methods.html#error.getseverity">Error::GetSeverity()</a>. Generally going to be 3 or 4.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>MessageText</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Informational text that will likely not be localized appropriately for the user.</p></td> </tr> </tbody> </table> <div class="paragraph"> <p>When 500 errors happen, a serious problem has occurred, which may mean an important subsystem is compromised. Do not expect a response body. The problem will have to be investigated on the server side. Your client should not attempt further communication with Helix Web Services.</p> </div> </div> <div class="sect2"> <h3 id="ruby_client_sdk_overview"><a class="link" href="#ruby_client_sdk_overview">Ruby Client SDK</a></h3> <div class="paragraph"> <p>If your application is written in Ruby, you can leverage our provided Ruby client SDK. This SDK is currently available as part of the Helix Web Services source code, in the <code>helix_web_services_client</code> directory. Many Helix Web Services methods refer to this client SDK as the <em>Ruby API</em>.</p> </div> <div class="sect3"> <h4 id="_basic_client_sdk_usage"><a class="link" href="#_basic_client_sdk_usage">Basic Client SDK usage</a></h4> <div class="paragraph"> <p>Our basic client leverages two methods:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p><code>execute_method_with_body</code></p> </li> <li> <p><code>execute_method_no_body</code></p> </li> </ol> </div> <div class="paragraph"> <p>Along with signing in via the constructore</p> </div> <div class="paragraph"> <p>These methods will generally parse the JSON response, and throw exceptions in case of any errors reported by the underlying server.</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-ruby" data-lang="ruby">require 'helix_web_services_client' client = HelixWebServicesClient.new({ :url => 'https://helix.example.com/hws', :user => 'jdoe', :password => 'joesupersecret' }) # Get starting list of depots depots = client.execute_method_no_body(:get, '/helix_versioning_engine/v1/files') # Print depot names depots.each { |d| puts d['name'] } # Make a user client.execute_method_with_body(:post, '/helix_versioning_engine/v1/users', nil, { 'User': 'new_user', 'FullName': 'Steve Austin', 'Email': 'saustin@example.com' })</code></pre> </div> </div> <div class="paragraph"> <p>You can additionally fetch the p4 ticket via the <code>client.ticket</code> property. If you store that, you can create a new <code>HelixWebServicesClient</code> specifying that via <code>ticket</code> instead of <code>password</code>.</p> </div> </div> <div class="sect3"> <h4 id="_models_in_the_client_sdk"><a class="link" href="#_models_in_the_client_sdk">Models in the Client SDK</a></h4> <div class="paragraph"> <p>Most HTTP methods that correspond to commands of the <code>p4</code> application, do not alter data from the Helix Versioning Engine. This can be inconvenient for application development. For example, dates are inconsistently represented, sometimes as strings, other times as timestamps. Keys that represent the same concept show up in different cases in the output data.</p> </div> <div class="paragraph"> <p>Because a single version of Helix Web Services can interact with multiple instances Helix Versioning Engines at different versions, we do not want to restrict the data coming out of the server. If you do maintain a single version of the Helix Versioning Engine that’s consistent with Helix Web Services, using model data can simplify your interaction. These are represented with 'typed' methods that will return classes that normalize data.</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-ruby" data-lang="ruby"># Get starting list of depots depots = client.depots # The depot name # # If you call the singular method using our basic API this key is sometimes # referred to by `name`, sometimes as `Depot` depots.first.depot # Instead of a number, returns a Ruby Date object depots.first.date # Can still make a user fairly easily client.create_user({ 'User': 'new_user', 'FullName': 'Steve Austin', 'Email': 'saustin@example.com' })</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_obtaining_the_client_sdk"><a class="link" href="#_obtaining_the_client_sdk">Obtaining the Client SDK</a></h4> <div class="paragraph"> <p>In order to use the client SDK, you’d first need to obtain a copy of the Helix Web Services source from the Perforce workshop. Visit the workshop at <a href="https://swarm.workshop.perforce.com/" class="bare">https://swarm.workshop.perforce.com/</a>. On that site, you can sign up a new account. Documentation on this process is provided via that website.</p> </div> <div class="paragraph"> <p>Once you have an account, you can then retrieve code via the Helix Versioning Engine available at <code>workshop.perforce.com:1666</code>. The source code is available at <code>//guest/perforce_software/helix-web-services/main/…​</code> Use any Perforce client application, such as <code>p4</code> or P4V to connect and retrieve the source code.</p> </div> <div class="paragraph"> <p>Official releases of Helix Web Services may publish this client SDK to rubygems. Stay tuned!</p> </div> </div> </div> </div> </div> <div class="sect1"> <h2 id="_http_protocol_reference"><a class="link" href="#_http_protocol_reference">HTTP Protocol Reference</a></h2> <div class="sectionbody"> <div class="paragraph"> <p>Each section includes methods related to a particular topic or resource.</p> </div> <div class="paragraph"> <p>Authentication Methods</p> </div> <div class="ulist"> <ul> <li> <p><a href="#post_auth_v1_login"><code>POST /auth/v1/login</code></a></p> </li> </ul> </div> <div class="paragraph"> <p>Helix Versioning Engine Methods</p> </div> <div class="paragraph"> <p>Branches</p> </div> <div class="ulist"> <ul> <li> <p><a href="#get_helix_versioning_engine_v1_branches"><code>GET /helix_versioning_engine/v1/branches</code></a></p> </li> <li> <p><a href="#post_helix_versioning_engine_v1_branches"><code>POST /helix_versioning_engine/v1/branches</code></a></p> </li> <li> <p><a href="#get_helix_versioning_engine_v1_branches_branch"><code>GET /helix_versioning_engine/v1/branches/[branch]</code></a></p> </li> <li> <p><a href="#patch_helix_versioning_engine_v1_branches_branch"><code>PATCH /helix_versioning_engine/v1/branches/[branch]</code></a></p> </li> <li> <p><a href="#delete_helix_versioning_engine_v1_branches_branch"><code>DELETE /helix_versioning_engine/v1/branches/[branch]</code></a></p> </li> </ul> </div> <div class="paragraph"> <p>Changes</p> </div> <div class="paragraph"> <p>Project Service Methods</p> </div> <div class="sect2"> <h3 id="_authentication_methods"><a class="link" href="#_authentication_methods">Authentication Methods</a></h3> <div class="sect3"> <h4 id="post_auth_v1_login"><a class="link" href="#post_auth_v1_login"><code>POST /auth/v1/login</code></a></h4> <div class="paragraph"> <p>Generates the Perforce login token for the server.</p> </div> <div class="sect4"> <h5 id="_request_headers"><a class="link" href="#_request_headers">Request Headers</a></h5> <table class="tableblock frame-all grid-all spread"> <colgroup> <col style="width: 50%;"> <col style="width: 50%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Header Name</th> <th class="tableblock halign-left valign-top">Notes</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Accept</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/json</code>, <code>text/plain</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Content-Type</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/json</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>X-Perforce-Helix_Web_Services-*</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">See <a href="#per_request_configuration">Per-Request Configuration</a></p></td> </tr> </tbody> </table> </div> <div class="sect4"> <h5 id="_request_body_json"><a class="link" href="#_request_body_json">Request Body JSON</a></h5> <div class="paragraph"> <p>A single JSON object with the following keys</p> </div> <table class="tableblock frame-all grid-all spread"> <colgroup> <col style="width: 50%;"> <col style="width: 50%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Property</th> <th class="tableblock halign-left valign-top">Description</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>user</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The Helix Versioning Engine login</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>password</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The password for the particular user</p></td> </tr> </tbody> </table> </div> <div class="sect4"> <h5 id="_response_headers"><a class="link" href="#_response_headers">Response Headers</a></h5> <table class="tableblock frame-all grid-all spread"> <colgroup> <col style="width: 50%;"> <col style="width: 50%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Header Name</th> <th class="tableblock halign-left valign-top">Notes</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Content-Type</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/json</code>, <code>text/plain</code></p></td> </tr> </tbody> </table> </div> <div class="sect4"> <h5 id="_response_body"><a class="link" href="#_response_body">Response Body</a></h5> <div class="paragraph"> <p>See <a href="#error_responses">Error Conventions</a> for the format of failures.</p> </div> <div class="paragraph"> <p>Upon success, when <code>Content-Type</code> is <code>text/plain</code>, the response body is simply the P4 ticket value.</p> </div> <div class="literalblock"> <div class="content"> <pre>16A76F005802E742E834163A22A4DED4</pre> </div> </div> <div class="paragraph"> <p>When <code>Content-Type</code> is <code>application/json</code>, the response is a JSON object.</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-json" data-lang="json">{ "ticket": "16A76F005802E742E834163A22A4DED4" }</code></pre> </div> </div> </div> </div> </div> <div class="sect2"> <h3 id="_branches"><a class="link" href="#_branches">Branches</a></h3> <div class="sect3"> <h4 id="get_helix_versioning_engine_v1_branches"><a class="link" href="#get_helix_versioning_engine_v1_branches"><code>GET /helix_versioning_engine/v1/branches</code></a></h4> <div class="paragraph"> <p>Lists available branches in the system. The resources of this list are summaries of branches in the system.</p> </div> <div class="sect4"> <h5 id="_request_headers_2"><a class="link" href="#_request_headers_2">Request Headers</a></h5> <table class="tableblock frame-all grid-all spread"> <colgroup> <col style="width: 50%;"> <col style="width: 50%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Header Name</th> <th class="tableblock halign-left valign-top">Notes</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Authorization</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">See <a href="#authentication">Authentication</a></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Accept</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/json</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Content-Type</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/json</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>X-Perforce-Helix_Web_Services-*</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">See <a href="#per_request_configuration">Per-Request Configuration</a></p></td> </tr> </tbody> </table> </div> <div class="sect4"> <h5 id="_response_headers_2"><a class="link" href="#_response_headers_2">Response Headers</a></h5> <table class="tableblock frame-all grid-all spread"> <colgroup> <col style="width: 50%;"> <col style="width: 50%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Header Name</th> <th class="tableblock halign-left valign-top">Notes</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Content-Type</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/json</code></p></td> </tr> </tbody> </table> </div> <div class="sect4"> <h5 id="_response_body_json"><a class="link" href="#_response_body_json">Response Body JSON</a></h5> <div class="paragraph"> <p>The values of this method are based on the result of the <a href="http://www.perforce.com/perforce/doc.current/manuals/cmdref/p4_branches.html"><code>p4 -ztag branches</code></a> command. For more field information refer to the <a href="http://www.perforce.com/perforce/doc.current/manuals/cmdref/p4_branches.html">command reference</a>.</p> </div> <div class="paragraph"> <p>See <a href="#error_responses">Error Conventions</a> for the format of failures.</p> </div> <div class="paragraph"> <p>An example response body might look like:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-json" data-lang="json">[ { "branch": "manually", "Update": "1437153920", "Access": "1437153920", "Owner": "jdoe", "Options": "unlocked", "Description": "Created by jdoe.\n" }, { "branch": "new_branch_CEUXQMYN", "Update": "1437164204", "Access": "1437164204", "Owner": "", "Options": "unlocked", "Description": "Something for the kids\n" } ]</code></pre> </div> </div> </div> <div class="sect4"> <h5 id="_ruby_client"><a class="link" href="#_ruby_client">Ruby Client</a></h5> <div class="paragraph"> <p>See <a href="./helix_web_services_client_ruby/HelixWebServicesClient.html#branches-instance_method"><code>HelixWebServicesClient#branches</code></a></p> </div> </div> </div> <div class="sect3"> <h4 id="post_helix_versioning_engine_v1_branches"><a class="link" href="#post_helix_versioning_engine_v1_branches"><code>POST /helix_versioning_engine/v1/branches</code></a></h4> <div class="paragraph"> <p>Creates a new branch specification, like the <code>p4 branch</code> command.</p> </div> <div class="paragraph"> <p>There are no other parameters to this method.</p> </div> <div class="sect4"> <h5 id="_request_headers_3"><a class="link" href="#_request_headers_3">Request Headers</a></h5> <table class="tableblock frame-all grid-all spread"> <colgroup> <col style="width: 50%;"> <col style="width: 50%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Header Name</th> <th class="tableblock halign-left valign-top">Notes</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Authorization</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">See <a href="#authentication">Authentication</a></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Accept</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/json</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Content-Type</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/json</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>X-Perforce-Helix_Web_Services-*</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">See <a href="#per_request_configuration">Per-Request Configuration</a></p></td> </tr> </tbody> </table> </div> <div class="sect4"> <h5 id="_request_body_json_2"><a class="link" href="#_request_body_json_2">Request Body JSON</a></h5> <div class="paragraph"> <p>Properties of the JSON object are the form fields of the <a href="http://www.perforce.com/perforce/doc.current/manuals/cmdref/p4_branch.html">p4 branch</a> command.</p> </div> </div> <div class="sect4"> <h5 id="_response_headers_3"><a class="link" href="#_response_headers_3">Response Headers</a></h5> <table class="tableblock frame-all grid-all spread"> <colgroup> <col style="width: 50%;"> <col style="width: 50%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Header Name</th> <th class="tableblock halign-left valign-top">Notes</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Content-Type</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/json</code></p></td> </tr> </tbody> </table> </div> <div class="sect4"> <h5 id="_example_request_json"><a class="link" href="#_example_request_json">Example Request JSON</a></h5> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-json" data-lang="json">{ "Branch": "my-project-main", "Description": "Branch my project from main to dev.\n", "View": ["//depot/main/my-project/... //depot/dev/my-project/..."] }</code></pre> </div> </div> </div> </div> <div class="sect3"> <h4 id="_ruby_client_example"><a class="link" href="#_ruby_client_example">Ruby Client Example</a></h4> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-ruby" data-lang="ruby">client.create_branch({ "Branch" => "my-project-main", "Description" => "Branch my project from main to dev.\n", "View" => ["//depot/main/my-project/... //depot/dev/my-project/..."] })</code></pre> </div> </div> <div class="sect4"> <h5 id="_ruby_client_2"><a class="link" href="#_ruby_client_2">Ruby Client</a></h5> <div class="paragraph"> <p>See <a href="./helix_web_services_client_ruby/HelixWebServicesClient.html#create_branch-instance_method"><code>HelixWebServicesClient#create_branch</code></a></p> </div> </div> </div> <div class="sect3"> <h4 id="get_helix_versioning_engine_v1_branches_branch"><a class="link" href="#get_helix_versioning_engine_v1_branches_branch"><code>GET /helix_versioning_engine/v1/branches/[branch]</code></a></h4> <div class="paragraph"> <p>Return branch details, similar to the <code>p4 branch -o</code> command.</p> </div> <div class="sect4"> <h5 id="_path_parameters"><a class="link" href="#_path_parameters">Path Parameters</a></h5> <table class="tableblock frame-all grid-all spread"> <colgroup> <col style="width: 20%;"> <col style="width: 20%;"> <col style="width: 20%;"> <col style="width: 20%;"> <col style="width: 20%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Parameter</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-left valign-top">Type</th> <th class="tableblock halign-left valign-top">Parameter Type</th> <th class="tableblock halign-left valign-top">Required</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">branch</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The branch name.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">path</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Yes</p></td> </tr> </tbody> </table> </div> <div class="sect4"> <h5 id="_request_headers_4"><a class="link" href="#_request_headers_4">Request Headers</a></h5> <table class="tableblock frame-all grid-all spread"> <colgroup> <col style="width: 50%;"> <col style="width: 50%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Header Name</th> <th class="tableblock halign-left valign-top">Notes</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Authorization</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">See <a href="#authentication">Authentication</a></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Accept</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/json</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Content-Type</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/json</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>X-Perforce-Helix_Web_Services-*</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">See <a href="#per_request_configuration">Per-Request Configuration</a></p></td> </tr> </tbody> </table> </div> <div class="sect4"> <h5 id="_response_headers_4"><a class="link" href="#_response_headers_4">Response Headers</a></h5> <table class="tableblock frame-all grid-all spread"> <colgroup> <col style="width: 50%;"> <col style="width: 50%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Header Name</th> <th class="tableblock halign-left valign-top">Notes</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Content-Type</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/json</code></p></td> </tr> </tbody> </table> </div> <div class="sect4"> <h5 id="_response_data"><a class="link" href="#_response_data">Response Data</a></h5> <div class="paragraph"> <p>Returns the form fields of the <a href="http://www.perforce.com/perforce/doc.current/manuals/cmdref/p4_branch.html"><code>p4 -ztag branch [branch name</a></code>] command. For more information, see the command reference. The main difference between the details and list view is the inclusion of the View field.</p> </div> </div> <div class="sect4"> <h5 id="_example_json_response"><a class="link" href="#_example_json_response">Example JSON Response</a></h5> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-json" data-lang="json">{ "Branch": "yagg-main", "Update": 1410907712, "Access": 1410907712, "Owner": "super", "Description": "Maintains a path from a development area to a main area.\n", "Options": "unlocked", "View": ["//depot/yagg/... //depot/main/yagg/..."] }</code></pre> </div> </div> </div> <div class="sect4"> <h5 id="_ruby_client_3"><a class="link" href="#_ruby_client_3">Ruby Client</a></h5> <div class="paragraph"> <p>See <a href="./helix_web_services_client_ruby/HelixWebServicesClient.html#branch-instance_method"><code>HelixWebServicesClient#branch</code></a></p> </div> </div> </div> <div class="sect3"> <h4 id="patch_helix_versioning_engine_v1_branches_branch"><a class="link" href="#patch_helix_versioning_engine_v1_branches_branch"><code>PATCH /helix_versioning_engine/v1/branches/[branch]</code></a></h4> <div class="paragraph"> <p>Update branch specifications, similar to the <code>p4 branch</code> command. Only the specified parameters in the body will be changed.</p> </div> <div class="sect4"> <h5 id="_url_parameters"><a class="link" href="#_url_parameters">URL Parameters</a></h5> <table class="tableblock frame-all grid-all spread"> <colgroup> <col style="width: 20%;"> <col style="width: 20%;"> <col style="width: 20%;"> <col style="width: 20%;"> <col style="width: 20%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Parameter</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-left valign-top">Type</th> <th class="tableblock halign-left valign-top">Parameter Type</th> <th class="tableblock halign-left valign-top">Required</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">branch</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The name of the branch</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">path</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Yes</p></td> </tr> </tbody> </table> </div> <div class="sect4"> <h5 id="_request_headers_5"><a class="link" href="#_request_headers_5">Request Headers</a></h5> <table class="tableblock frame-all grid-all spread"> <colgroup> <col style="width: 50%;"> <col style="width: 50%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Header Name</th> <th class="tableblock halign-left valign-top">Notes</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Authorization</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">See <a href="#authentication">Authentication</a></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Accept</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/json</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Content-Type</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/json</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>X-Perforce-Helix_Web_Services-*</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">See <a href="#per_request_configuration">Per-Request Configuration</a></p></td> </tr> </tbody> </table> </div> <div class="sect4"> <h5 id="_request_body_json_3"><a class="link" href="#_request_body_json_3">Request Body JSON</a></h5> <div class="paragraph"> <p>A single JSON object containing form fields of the <a href="http://www.perforce.com/perforce/doc.current/manuals/cmdref/p4_branch.html"><code>p4 -ztag branch [branch name</a></code>] command.</p> </div> </div> <div class="sect4"> <h5 id="_response_headers_5"><a class="link" href="#_response_headers_5">Response Headers</a></h5> <table class="tableblock frame-all grid-all spread"> <colgroup> <col style="width: 50%;"> <col style="width: 50%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Header Name</th> <th class="tableblock halign-left valign-top">Notes</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Content-Type</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/json</code></p></td> </tr> </tbody> </table> </div> <div class="sect4"> <h5 id="_example_json_request_body"><a class="link" href="#_example_json_request_body">Example JSON Request Body</a></h5> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-json" data-lang="json">{ "Description": "The updated my-project-main description.\n", "Owner": "saustin" }</code></pre> </div> </div> </div> <div class="sect4"> <h5 id="_ruby_client_4"><a class="link" href="#_ruby_client_4">Ruby Client</a></h5> <div class="paragraph"> <p>See <a href="./helix_web_services_client_ruby/HelixWebServicesClient.html#update_branch-instance_method"><code>HelixWebServicesClient#update_branch</code></a></p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-ruby" data-lang="ruby">client.update_branch({ "Branch" => 'my_branch_id', "Description": "The updated my-project-main description.\n", "Owner": "saustin" })</code></pre> </div> </div> </div> <div class="sect4"> <h5 id="delete_helix_versioning_engine_v1_branches_branch"><a class="link" href="#delete_helix_versioning_engine_v1_branches_branch"><code>DELETE /helix_versioning_engine/v1/branches/[branch]</code></a></h5> <div class="paragraph"> <p>Removes the branch specification, similar to the <code>p4 branch -d</code> command.</p> </div> <div class="sect5"> <h6 id="_url_parameters_2"><a class="link" href="#_url_parameters_2">URL Parameters</a></h6> <table class="tableblock frame-all grid-all spread"> <colgroup> <col style="width: 20%;"> <col style="width: 20%;"> <col style="width: 20%;"> <col style="width: 20%;"> <col style="width: 20%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Parameter</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-left valign-top">Type</th> <th class="tableblock halign-left valign-top">Parameter Type</th> <th class="tableblock halign-left valign-top">Required</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">branch</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The name of the branch</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">path</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Yes</p></td> </tr> </tbody> </table> </div> </div> <div class="sect4"> <h5 id="_request_headers_6"><a class="link" href="#_request_headers_6">Request Headers</a></h5> <table class="tableblock frame-all grid-all spread"> <colgroup> <col style="width: 50%;"> <col style="width: 50%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Header Name</th> <th class="tableblock halign-left valign-top">Notes</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Authorization</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">See <a href="#authentication">Authentication</a></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Accept</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/json</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Content-Type</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/json</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>X-Perforce-Helix_Web_Services-*</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">See <a href="#per_request_configuration">Per-Request Configuration</a></p></td> </tr> </tbody> </table> </div> <div class="sect4"> <h5 id="_response_headers_6"><a class="link" href="#_response_headers_6">Response Headers</a></h5> <table class="tableblock frame-all grid-all spread"> <colgroup> <col style="width: 50%;"> <col style="width: 50%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Header Name</th> <th class="tableblock halign-left valign-top">Notes</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Content-Type</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/json</code></p></td> </tr> </tbody> </table> </div> <div class="sect4"> <h5 id="_ruby_client_5"><a class="link" href="#_ruby_client_5">Ruby Client</a></h5> <div class="paragraph"> <p>See <a href="./helix_web_services_client_ruby/HelixWebServicesClient.html#delete_branch-instance_method"><code>HelixWebServicesClient#delete_branch}</code></a></p> </div> </div> </div> </div> </div> </div> <div class="sect1"> <h2 id="_client_sdk_reference"><a class="link" href="#_client_sdk_reference">Client SDK Reference</a></h2> <div class="sectionbody"> <div class="paragraph"> <p>The most complete Client SDK is provided by Ruby.</p> </div> <div class="ulist"> <ul> <li> <p><a href="./helix_web_services_client_ruby/index.html">Open Ruby Client API</a></p> </li> </ul> </div> <div class="paragraph"> <p>Other SDKs are available that provide a less complete implementation to Helix Web SErvices</p> </div> <div class="ulist"> <ul> <li> <p><a href="./helix_web_services_client_js/index.html">Open JavaScript API</a></p> </li> <li> <p><a href="./helix_web_services_client_qt/html/index.html">Open Qt API</a></p> </li> </ul> </div> </div> </div> <div class="sect1"> <h2 id="_server_programming"><a class="link" href="#_server_programming">Server Programming</a></h2> <div class="sectionbody"> <div class="sect2"> <h3 id="_including_new_rack_middleware"><a class="link" href="#_including_new_rack_middleware">Including New Rack Middleware</a></h3> </div> <div class="sect2"> <h3 id="_registering_configuration_variables"><a class="link" href="#_registering_configuration_variables">Registering Configuration Variables</a></h3> </div> </div> </div> <div class="sect1"> <h2 id="_server_api_reference"><a class="link" href="#_server_api_reference">Server API Reference</a></h2> <div class="sectionbody"> <div class="paragraph"> <p>The Ruby-based server API reference should help you when generating custom services to be deployed into your own environment.</p> </div> <div class="ulist"> <ul> <li> <p><a href="./helix_web_services/index.html">Open Server API</a></p> </li> </ul> </div> </div> </div> </div> <div id="footer"> <div id="footer-text"> Version 2015.1.alpha<br> Last updated 2015-07-16 18:36:50 PDT </div> </div> </body> </html>
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#69 | 16346 | tjuricek | Removing build directory prior to migration | ||
#68 | 16334 | tjuricek |
HWS/NOARCH/2015.1.main/201510211937 Documentation updates for new system configuration options. |
||
#67 | 16331 | tjuricek |
HWS/NOARCH/2015.1.main/201510211755 This is a large revision to the deployment: - nginx is now part of the system - perforce-server is *not* installed during post-install configuration The only change for migration should be to uninstall (and purge) your current nginx setup. |
||
#66 | 16161 | tjuricek |
HWS/NOARCH/2015.1.main/201510082242 - Ability for custom middleware to filter P4PORT agressively - If p4d deems login not necessary, return an empty string for the valid ticket value. |
||
#65 | 16144 | tjuricek |
HWS/NOARCH/2015.1.main/201510081946 Major revision to URL structure. This is a breaking change to the API. |
||
#64 | 16093 | tjuricek |
HWS/NOARCH/2015.1.main/201510061647 Rename of trust setting, and adjustments to Helix Sync logic for selecting pending changelists |
||
#63 | 16077 | tjuricek |
HWS/NOARCH/2015.1.main/201510051630 Includes changes to trust behavior |
||
#62 | 16007 | tjuricek |
HWS/NOARCH/2015.1.main/201510011648 Documentation updates for the new default parameter to getting the latest helix sync client |
||
#61 | 15975 | tjuricek |
HWS/NOARCH/2015.1.main/201509291925 Add support for repo creation/update and deletion, same for SSH keys. Add util module for supporting methods, modify temp client to dissapear. (Modified submit of review 15549 by @ptomiak) |
||
#60 | 15783 | tjuricek | HWS/NOARCH/2015.1.main/201509180120 | ||
#59 | 15769 | tjuricek | HWS/NOARCH/2015.1.main/201509170120 | ||
#58 | 15758 | tjuricek | HWS/NOARCH/2015.1.main/201509161743 | ||
#57 | 15737 | tjuricek | HWS/NOARCH/2015.1.main/201509150120 | ||
#56 | 15708 | tjuricek | HWS/NOARCH/2015.1.main/201509140120 | ||
#55 | 15699 | tjuricek | HWS/NOARCH/2015.1.main/201509111845 | ||
#54 | 15690 | tjuricek | HWS/NOARCH/2015.1.main/201509110141 | ||
#53 | 15676 | tjuricek | HWS/NOARCH/2015.1.main/201509092205 | ||
#52 | 15545 | tjuricek | Place in config and hooks for Helix Cloud authentication. | ||
#51 | 15530 | tjuricek | Add basic development guide | ||
#50 | 15517 | tjuricek | Do not require changelist ID for submitting Helix Sync pending changelists. | ||
#49 | 15499 | tjuricek | Naive implementation of helix sync project submit for "helix versioning engine projects". | ||
#48 | 15496 | tjuricek |
Revise GET /helix_sync/v1/changes/[project] to /helix_sync/v1/changes/[project]/pending The base method is really intended for the latest changelist number. Meh. |
||
#47 | 15487 | tjuricek | Add basic ability to create pending changelists for helix sync projects. | ||
#46 | 15483 | tjuricek |
Add a new 'hws_console` application to the deployment. This should give users the ability to use the Ruby client for quick and dirty debugging of issues with HWS. Since it's so easy, I've added it to the deployment guide. |
||
#45 | 15479 | tjuricek | Added a basic "HVE project" implementation for creating clients. | ||
#44 | 15460 | tjuricek | Add notes on service startup in deployment | ||
#43 | 15447 | tjuricek |
Add simple Example application to list "projects" in a HVE instance. Qt's a little weird to follow, so I may have to find a different kind of example to write. It does work, however. |
||
#42 | 15435 | tjuricek |
Add proposed HTTP methods for Helix Sync. It's a little unclear to me why you would need a local root directory to create the shared shelving changelist for a particular project (and user). So I didn't add that. |
||
#41 | 15423 | tjuricek |
Revised HWS Qt API. This is a major revision of the API, which removes most of the "typed" data, replacing it with a more generic "executeMethodDone" callback. The main benefit here is to allow the API to interop with different versions of p4d, and not restrict the methods it can call. We may add more helpers in the future. |
||
#40 | 15299 | tjuricek | Revise documentation of our new 'list available services' method. | ||
#39 | 15297 | tjuricek |
Implement of 'cluster services' configuration. The configuration will be stored in a local JSON file, which is expected to be maintained by the systems admin. Eventually, it's expected to have this sort of thing implemented via Helix Admin. |
||
#38 | 15260 | tjuricek |
Fix truncation of content when listing a single file's details. Added message warning people about timeouts. This does work, and doesn't bring the system down, but you'll probably hit timeouts if you're trying to download files using this system. |
||
#37 | 15257 | tjuricek |
Added stress test, corrected per-request header config. Apparently using underscores is a "special" mechanism for HTTP headers, and requires adjusting nginx to allow such things. Might as well just recommend using hyphens, which get converted to underscores anyway. The current test just hits a listing of 20000 files against p4play. Returns a 2.5 MB response, which doesn't seem to cause problems (yay). |
||
#36 | 15242 | tjuricek | Add Helix Sync stubs and documentation | ||
#35 | 15241 | tjuricek | Add Git Fusion stubs and documentation. | ||
#34 | 15240 | tjuricek |
Set api level via request path on all Helix Versioning Engine methods. This will allow migration of applications to different P4D versions. Our internal methods (like project API) should attempt to handle backward compatibility similarly. P4WEBAPI-118 |
||
#33 | 15229 | tjuricek | Remove incorrect default setting for P4HOST | ||
#32 | 15228 | tjuricek | Revise triggers implementation, tests, and documentation. | ||
#31 | 15227 | tjuricek |
Revise implementation, tests, and documentation for protections management. Remove some specs I will not be revising from the helix_web_services project. |
||
#30 | 15225 | tjuricek |
Revise counter implementation, tests, and documentation Wasn't available in the Ruby client before, so, it's now available. |
||
#29 | 15222 | tjuricek |
Revise server specs testing and documentation. Note: also fixed issues with setting P4PORT via headers. For whatever reason, the host setting doesn't seem to work like I expect it to, though P4PORT works just fine. |
||
#28 | 15211 | tjuricek | Implement tests and documentation for label spec management. | ||
#27 | 15210 | tjuricek | Implement tests and documentation for job spec management. | ||
#26 | 15209 | tjuricek | Implement tests and documentation for group spec management. | ||
#25 | 15208 | tjuricek |
Revise 'command' implementation, tests, and documentaiton. This includes a change from a command blacklist to a whitelist. See P4WEBAPI-21 |
||
#24 | 15205 | tjuricek | Implemented tests and documentation for depot spec editing. | ||
#23 | 15189 | tjuricek | Update files implementation, testing, and documentation. | ||
#22 | 15188 | tjuricek | Execute user modification commands as the super user in tests. | ||
#21 | 15185 | tjuricek | Update user spec management implementation, tests, and documentation. | ||
#20 | 15144 | tjuricek |
Setup stream spec tests and documentation. Also revised the documentation folder http_p4_web_api -> helix_versioning_engine |
||
#19 | 15132 | tjuricek | Provde a basic submit -e mechanism on classic perforce workspaces. | ||
#18 | 15110 | tjuricek | Revise changes methods for new p4 connection handling, add server specs, remove model references in client, and update asciidoc documentation. | ||
#17 | 15099 | tjuricek | Revise project services to be our simple 'container' for other systems. | ||
#16 | 15098 | tjuricek |
Revised project services to GET-only forms. With Helix Sync revising to integrate purely with Helix Cloud, this is the only thing we can reasonably define. |
||
#15 | 15090 | tjuricek |
Update _proposed_ API for project services. This is *very likely* to change, and will not be implemented until reviewed. |
||
#14 | 15078 | tjuricek |
clients spec method revisions Updated some other documentation. |
||
#13 | 15077 | tjuricek |
Add new 'model' technique, revised branch spec operations, test Auth::Middleware. The Ruby client now does *not* strictly type anything, but extends OpenStruct with helper methods to help deal with inconsistent data formats. See the OpenModel class documentation for more details. The Auth::Middleware class is also *finally* implemented as well. This does not take into account all possible variations of server behavior (yet), but that will happen in follow-up work. |
||
#12 | 15073 | tjuricek | Update Auth::Middleware and add spec | ||
#11 | 15042 | tjuricek | Document error conventions. | ||
#10 | 15038 | tjuricek | Document 'login' auth method and client programming overview. | ||
#9 | 15032 | tjuricek |
Starting config and doc revisions. System is now broken while revisions underway. Configuration of the p4d connection is now done via a single HWSSettings middleware object injected into the Rack env. The HWSP4Cleanup middleware now cleans up any p4 injected into the Rack env. The Auth::App class now mostly just contains one method to generate a p4 ticket. /auth/v1/login. Added yard documentation for the main project. Yard docs have been reconfigured to dump into build/ directories. This should probably be done with each release. Hm... The top level rake file contains a task, 'all:doc', to update our documentation. This should probably be run for each checkin. Hm... Specs are now using Rack::Test on top of a 'live' p4d. I'd suggest you still use the p4util mechanism, which now dumps to a /tmp folder, so we can safely add P4IGNORE rules back into your local .p4config file. Old 'perforce' application now called 'helix_versioning_engine'. Removing cache data. Helix Sync may be slow. It may also get axed. We'll see. |
||
#8 | 14980 | tjuricek |
Starting to make revisions to the Asciidoc guide. These are just revisions to the preable sections. |
||
#7 | 14184 | tjuricek | Revise the CD diagram, update official docs, and change the DB configuration key. | ||
#6 | 14182 | tjuricek | Asciidoc conversion of the changes HTTP guide | ||
#5 | 14049 | tjuricek |
Add methods to generate client workspaces for a user. The Qt SDK was updated based on immediate need. Also, add Ruby client SDK documentation to the docs site. Everything is early, but there's *some* reference available at least. |
||
#4 | 14027 | tjuricek | Move almost all Qt SDK documentation to Doxygen system, and just reference it in the main doc site. | ||
#3 | 14002 | tjuricek | Some preliminary API documentation for the JavaScript SDK. | ||
#2 | 13614 | tjuricek | Update the Qt guide to include the QSettings* constructor. | ||
#1 | 13612 | tjuricek | Update deployment guide, switch built documentation to asciidoc, remove unused packaging script for p4_web_api |